From 597bfba3b4946d15fa74d6af548f009cd53c461e Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Wed, 2 Aug 2017 23:37:56 +0800 Subject: [PATCH 1/2] feat: use getByte() instead of get() - improve read performance https://github.com/node-modules/byte/pull/29 - use fastbench instead of benchmark --- .gitignore | 3 +- benchmark/decode.js | 14 ----- benchmark/decoder.v1.read.js | 91 +++++++++++++++++++++++++++++ benchmark/encode.js | 36 +++++++----- benchmark/encoder.v1.write.js | 61 +++++++++++++++++++ benchmark/encoder.v2.write.js | 61 +++++++++++++++++++ benchmark/encoder.v2.writeString.js | 27 +++++++++ lib/v1/decoder.js | 16 +---- lib/v1/encoder.js | 13 +---- lib/v2/decoder.js | 60 ++++++++----------- lib/v2/encoder.js | 19 +----- package.json | 4 +- 12 files changed, 296 insertions(+), 109 deletions(-) create mode 100644 benchmark/decoder.v1.read.js create mode 100644 benchmark/encoder.v1.write.js create mode 100644 benchmark/encoder.v2.write.js create mode 100644 benchmark/encoder.v2.writeString.js diff --git a/.gitignore b/.gitignore index 1f29639..8438bc1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ node_modules .DS_Store coverage.html coverage/ -.idea/ \ No newline at end of file +.idea/ +profile-*/ diff --git a/benchmark/decode.js b/benchmark/decode.js index bb92f19..8eb4814 100644 --- a/benchmark/decode.js +++ b/benchmark/decode.js @@ -1,19 +1,5 @@ -/**! - * hessian.js - benchmark/decode.js - * - * Copyright(c) fengmk2 and other contributors. - * MIT Licensed - * - * Authors: - * fengmk2 (http://fengmk2.github.com) - */ - 'use strict'; -/** - * Module dependencies. - */ - var ByteBuffer = require('byte'); var Benchmark = require('benchmark'); var benchmarks = require('beautify-benchmark'); diff --git a/benchmark/decoder.v1.read.js b/benchmark/decoder.v1.read.js new file mode 100644 index 0000000..95b1863 --- /dev/null +++ b/benchmark/decoder.v1.read.js @@ -0,0 +1,91 @@ +'use strict'; + +var bench = require('fastbench'); +var java = require('js-to-java'); +var hessian = require('..'); + +var max = 10; + +var complexObject = { + $class: 'com.hessiantest.org.MockRequest', + $: { + id: 123, + name: 'getData', + args: [1, makeStr('name', 1), makeStr('a', 200)], + conn: { + $class: 'com.hessiantest.org.MockRequestConnection', + $: { + ctx: java.long(1024), + name: makeStr('p', 200), + } + } + } +}; +var complexObjectBuf = hessian.encode(complexObject, '1.0'); +console.log(JSON.stringify(hessian.decode(complexObjectBuf, '1.0', true), null, 2)); + +var run = bench([ + function readComplexObject(cb) { + for (var i = 0; i < max; i++) { + hessian.decode(complexObjectBuf, '1.0', true); + } + setImmediate(cb); + }, +], 10000); + +run(run); + +function makeStr(str, concats) { + var s = '' + while (concats--) { + s += str + } + return s +} + +// node benchmark/decoder.v1.read.js +// { +// "$class": "com.hessiantest.org.MockRequest", +// "$": { +// "id": { +// "$class": "int", +// "$": 123 +// }, +// "name": { +// "$class": "java.lang.String", +// "$": "getData" +// }, +// "args": { +// "$class": "java.util.ArrayList", +// "$": [ +// { +// "$class": "int", +// "$": 1 +// }, +// { +// "$class": "java.lang.String", +// "$": "name" +// }, +// { +// "$class": "java.lang.String", +// "$": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +// } +// ] +// }, +// "conn": { +// "$class": "com.hessiantest.org.MockRequestConnection", +// "$": { +// "ctx": { +// "$class": "long", +// "$": 1024 +// }, +// "name": { +// "$class": "java.lang.String", +// "$": "pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp" +// } +// } +// } +// } +// } +// readComplexObject*10000: 2062.029ms +// readComplexObject*10000: 1711.482ms diff --git a/benchmark/encode.js b/benchmark/encode.js index 0c9a568..cfa1d75 100644 --- a/benchmark/encode.js +++ b/benchmark/encode.js @@ -1,19 +1,5 @@ -/**! - * hessian.js - benchmark/encode.js - * - * Copyright(c) fengmk2 and other contributors. - * MIT Licensed - * - * Authors: - * fengmk2 (http://fengmk2.github.com) - */ - 'use strict'; -/** - * Module dependencies. - */ - var ByteBuffer = require('byte'); var Benchmark = require('benchmark'); var benchmarks = require('beautify-benchmark'); @@ -100,6 +86,20 @@ suite hessian.encode(complexObject, '1.0'); }) .add('hessian2 encode: complex object', function() { + var complexObject = { + $class: 'com.hessiantest.org.MockRequest', + $: { + id: 123, + name: 'getData', + args: [1, makeStr('name', 1), makeStr('a', 200)], + conn: { + $class: 'com.hessiantest.org.MockRequestConnection', + $: { + ctx: java.long(1024) + } + } + } + }; hessian.encode(complexObject, '2.0'); }) @@ -177,3 +177,11 @@ suite // hessian2 encode: simple object x 155,580 ops/sec ±0.82% (98 runs sampled) // hessian1 encode: complex object x 103,974 ops/sec ±1.34% (96 runs sampled) // hessian2 encode: complex object x 100,160 ops/sec ±1.18% (101 runs sampled) + +function makeStr(str, concats) { + var s = '' + while (concats--) { + s += str + } + return s +} diff --git a/benchmark/encoder.v1.write.js b/benchmark/encoder.v1.write.js new file mode 100644 index 0000000..ca03009 --- /dev/null +++ b/benchmark/encoder.v1.write.js @@ -0,0 +1,61 @@ +'use strict'; + +var bench = require('fastbench'); +var java = require('js-to-java'); +var hessian = require('..'); + +var max = 10; + +var complexObject = { + $class: 'com.hessiantest.org.MockRequest', + $: { + id: 123, + name: 'getData', + args: [1, makeStr('name', 1), makeStr('a', 200)], + conn: { + $class: 'com.hessiantest.org.MockRequestConnection', + $: { + ctx: java.long(1024) + } + } + } +}; +console.log(hessian.encode(complexObject, '1.0').length, hessian.encode(complexObject, '1.0')); + +var run = bench([ + function writeComplexObject(cb) { + for (var i = 0; i < max; i++) { + var complexObject = { + $class: 'com.hessiantest.org.MockRequest', + $: { + id: 123, + name: 'getData', + args: [1, makeStr('name', 1), makeStr('a', 200)], + conn: { + $class: 'com.hessiantest.org.MockRequestConnection', + $: { + ctx: java.long(1024) + } + } + } + }; + hessian.encode(complexObject, '1.0'); + } + setImmediate(cb); + }, +], 10000); + +run(run); + +function makeStr(str, concats) { + var s = '' + while (concats--) { + s += str + } + return s +} + +// node benchmark/encoder.v1.write.js +// 360 +// writeComplexObject*10000: 1214.816ms +// writeComplexObject*10000: 1151.431ms diff --git a/benchmark/encoder.v2.write.js b/benchmark/encoder.v2.write.js new file mode 100644 index 0000000..8b9aec1 --- /dev/null +++ b/benchmark/encoder.v2.write.js @@ -0,0 +1,61 @@ +'use strict'; + +var bench = require('fastbench'); +var java = require('js-to-java'); +var hessian = require('..'); + +var max = 10; + +var complexObject = { + $class: 'com.hessiantest.org.MockRequest', + $: { + id: 123, + name: 'getData', + args: [1, makeStr('name', 1), makeStr('a', 200)], + conn: { + $class: 'com.hessiantest.org.MockRequestConnection', + $: { + ctx: java.long(1024) + } + } + } +}; +console.log(hessian.encode(complexObject, '2.0').length, hessian.encode(complexObject, '2.0')); + +var run = bench([ + function writeComplexObject(cb) { + for (var i = 0; i < max; i++) { + var complexObject = { + $class: 'com.hessiantest.org.MockRequest', + $: { + id: 123, + name: 'getData', + args: [1, makeStr('name', 1), makeStr('a', 200)], + conn: { + $class: 'com.hessiantest.org.MockRequestConnection', + $: { + ctx: java.long(1024) + } + } + } + }; + hessian.encode(complexObject, '2.0'); + } + setImmediate(cb); + }, +], 10000); + +run(run); + +function makeStr(str, concats) { + var s = '' + while (concats--) { + s += str + } + return s +} + +// node benchmark/encoder.v2.write.js +// 324 +// writeComplexObject*10000: 1162.727ms +// writeComplexObject*10000: 1032.622ms diff --git a/benchmark/encoder.v2.writeString.js b/benchmark/encoder.v2.writeString.js new file mode 100644 index 0000000..e754511 --- /dev/null +++ b/benchmark/encoder.v2.writeString.js @@ -0,0 +1,27 @@ +'use strict'; + +var bench = require('fastbench'); +var hessian = require('..'); + +var max = 10; + +console.log(hessian.encode(makeStr('a', 200), '2.0')); + +var run = bench([ + function writeSmallString(cb) { + for (var i = 0; i < max; i++) { + hessian.encode(makeStr('a', 200), '2.0'); + } + setImmediate(cb); + }, +], 10000); + +run(run); + +function makeStr(str, concats) { + var s = '' + while (concats--) { + s += str + } + return s +} diff --git a/lib/v1/decoder.js b/lib/v1/decoder.js index 67f22d4..6581d5a 100644 --- a/lib/v1/decoder.js +++ b/lib/v1/decoder.js @@ -1,13 +1,3 @@ -/** - * hessian.js - lib/v1/decoder.js - * Copyright(c) - * MIT Licensed - * - * Authors: - * dead_horse (http://deadhorse.me) - * fengmk2 (http://fengmk2.github.com) - */ - 'use strict'; var debug = require('debug')('hessian:v1:decoder'); @@ -261,7 +251,7 @@ proto._readUTF8String = function (len) { } while (len--) { - head = this.byteBuffer.get(); + head = this.byteBuffer.getByte(); l = utils.lengthOfUTF8(head); this.byteBuffer.skip(l - 1); } @@ -286,11 +276,11 @@ proto._readUTF8String = function (len) { */ proto.readString = function (withType) { var str = ''; - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); // get all trunk start with 's' while (code === 0x73) { str += this._readUTF8String(); - code = this.byteBuffer.get(); + code = this.byteBuffer.getByte(); } if (code === 0x53) { diff --git a/lib/v1/encoder.js b/lib/v1/encoder.js index 60a9596..7f1f555 100644 --- a/lib/v1/encoder.js +++ b/lib/v1/encoder.js @@ -1,13 +1,3 @@ -/** - * hessian.js - lib/encoder.js - * Copyright(c) - * MIT Licensed - * - * Authors: - * dead_horse (http://deadhorse.me) - * fengmk2 (http://fengmk2.github.com) - */ - 'use strict'; var ByteBuffer = require('byte'); @@ -156,7 +146,6 @@ proto.writeBytes = function (buf) { * S 0x00 0x03 [0x01 0x02 0x03] */ proto.writeString = function (str) { - this._assertType('writeString', 'string', str); var offset = 0; var length = str.length; @@ -307,7 +296,7 @@ proto._writeObject = function (obj) { * : {$class: 'java.lang.Map', $: {a: 1}} */ proto.writeObject = function (obj) { - if (is.nullOrUndefined(obj) || + if (is.nullOrUndefined(obj) || // : { a: { '$class': 'xxx', '$': null } } (is.string(obj.$class) && is.nullOrUndefined(obj.$))) { debug('writeObject with a null'); diff --git a/lib/v2/decoder.js b/lib/v2/decoder.js index 630d4b6..8db80a7 100644 --- a/lib/v2/decoder.js +++ b/lib/v2/decoder.js @@ -1,18 +1,4 @@ -/** - * hessian.js - lib/v2/decoder.js - * - * Copyright(c) - * MIT Licensed - * - * Authors: - * fengmk2 (http://fengmk2.github.com) - */ - -"use strict"; - -/** - * Module dependencies. - */ +'use strict'; var util = require('util'); var is = require('is-type-of'); @@ -96,7 +82,7 @@ utils.addByteCodes(BYTE_CODES, [ * @api public */ proto.readInt = function () { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); // Compact int if (code >= 0x80 && code <= 0xbf) { // Integers between -16 and 47 can be encoded by a single octet in the range x80 to xbf. @@ -106,13 +92,13 @@ proto.readInt = function () { if (code >= 0xc0 && code <= 0xcf) { // Integers between -2048 and 2047 can be encoded in two octets with the leading byte in the range xc0 to xcf. // value = ((code - 0xc8) << 8) + b0; - return ((code - 0xc8) << 8) + this.byteBuffer.get(); + return ((code - 0xc8) << 8) + this.byteBuffer.getByte(); } if (code >= 0xd0 && code <= 0xd7) { // Integers between -262144 and 262143 can be encoded in three bytes with the leading byte in the range xd0 to xd7. // value = ((code - 0xd4) << 16) + (b1 << 8) + b0; - var b1 = this.byteBuffer.get(); - var b0 = this.byteBuffer.get(); + var b1 = this.byteBuffer.getByte(); + var b0 = this.byteBuffer.getByte(); return ((code - 0xd4) << 16) + (b1 << 8) + b0; } if (code === 0x49) { @@ -173,7 +159,7 @@ utils.addByteCodes(BYTE_CODES, [ * @api public */ proto.readLong = function () { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); // Compact long if (code >= 0xd8 && code <= 0xef) { // Longs between -8 and 15 are represented by a single octet in the range xd8 to xef. @@ -183,13 +169,13 @@ proto.readLong = function () { if (code >= 0xf0 && code <= 0xff) { // Longs between -2048 and 2047 are encoded in two octets with the leading byte in the range xf0 to xff. // value = ((code - 0xf8) << 8) + b0 - return ((code - 0xf8) << 8) + this.byteBuffer.get(); + return ((code - 0xf8) << 8) + this.byteBuffer.getByte(); } if (code >= 0x38 && code <= 0x3f) { // Longs between -262144 and 262143 are encoded in three octets with the leading byte in the range x38 to x3f. // value = ((code - 0x3c) << 16) + (b1 << 8) + b0 - var b1 = this.byteBuffer.get(); - var b0 = this.byteBuffer.get(); + var b1 = this.byteBuffer.getByte(); + var b0 = this.byteBuffer.getByte(); return ((code - 0x3c) << 16) + (b1 << 8) + b0; } if (code === 0x59) { @@ -250,7 +236,7 @@ utils.addByteCodes(BYTE_CODES, [ * @api public */ proto.readDouble = function () { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); if (code === 0x44) { return this.byteBuffer.getDouble(); } @@ -298,7 +284,7 @@ utils.addByteCodes(BYTE_CODES, [ * @api public */ proto.readDate = function () { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); if (code === 0x4a) { return new Date(utils.handleLong(this.byteBuffer.getLong())); } @@ -339,7 +325,7 @@ utils.addByteCodes(BYTE_CODES, [ * @api public */ proto.readBytes = function () { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); if (code >= 0x20 && code <= 0x2f) { // short binary var len = code - 0x20; @@ -352,7 +338,7 @@ proto.readBytes = function () { while (code === 0x41) { length = this.byteBuffer.getUInt16(); bufs.push(this.byteBuffer.read(length)); - code = this.byteBuffer.get(); + code = this.byteBuffer.getByte(); } if (code === 0x42) { @@ -363,7 +349,7 @@ proto.readBytes = function () { length = code - 0x20; bufs.push(this.byteBuffer.read(length)); } else if (code >= 0x34 && code <= 0x37) { - length = (code - 0x34) * 256 + this.byteBuffer.get(); + length = (code - 0x34) * 256 + this.byteBuffer.getByte(); bufs.push(this.byteBuffer.read(length)); } else { this.throwError('readBytes', code); @@ -411,7 +397,7 @@ utils.addByteCodes(BYTE_CODES, [ */ proto.readString = function () { var str = ''; - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); debug('readString() code: %s', code); var length; @@ -434,7 +420,7 @@ proto.readString = function () { case 0x30: case 0x31: case 0x32: case 0x33: this._isLastChunk = true; - length = (code - 0x30) * 256 + this.byteBuffer.get(); + length = (code - 0x30) * 256 + this.byteBuffer.getByte(); str += this._readUTF8String(length); break; @@ -559,7 +545,7 @@ proto._readObjectDefinition = function () { * @api public */ proto.readObject = function (withType) { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); var ref; if (code === 0x43) { // C(x43) type fields-length(writeInt) fields-names(writeString) o ref fields-values(write) @@ -572,7 +558,7 @@ proto.readObject = function (withType) { } else { this.throwError('readObject', code); } - + var cls = this.classes[ref]; debug('readObject %s, ref: %s', cls.name, ref); @@ -618,7 +604,7 @@ utils.addByteCodes(BYTE_CODES, [ * @return {Number} */ proto.readRefId = function (withType) { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); if (code === 0x51) { return this.readInt(); } @@ -666,7 +652,7 @@ proto._readFixedLengthItems = function (len, list, withType) { * @api public */ proto.readArray = function (withType) { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); var type; var result; var list = []; @@ -752,7 +738,7 @@ proto._readMap = function (map, withType) { */ proto.readHashMap = function (withType) { // H: x48 - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); if (code !== 0x48) { this.throwError('readHashMap', code); } @@ -773,7 +759,7 @@ utils.addByteCodes(BYTE_CODES, [ * v2.0 * ``` * map ::= M(x4d) [type] (value value)* Z - * + * * @see http://hessian.caucho.com/doc/hessian-serialization.html##map * ``` * Represents serialized maps and can represent objects. @@ -792,7 +778,7 @@ utils.addByteCodes(BYTE_CODES, [ * @api public */ proto.readMap = function (withType) { - var code = this.byteBuffer.get(); + var code = this.byteBuffer.getByte(); if (code !== 0x4d) { this.throwError('readMap', code); } diff --git a/lib/v2/encoder.js b/lib/v2/encoder.js index be7dcc6..baed5a9 100644 --- a/lib/v2/encoder.js +++ b/lib/v2/encoder.js @@ -1,20 +1,7 @@ -/** - * hessian.js - lib/v2/encoder.js - * - * Copyright(c) - * MIT Licensed - * - * Authors: - * fengmk2 (http://fengmk2.github.com) - */ - -"use strict"; - -/** - * Module dependencies. - */ +'use strict'; var debug = require('debug')('hessian:v2:encoder'); +var flatstr = require('flatstr'); var is = require('is-type-of'); var util = require('util'); var EncoderV1 = require('../v1/encoder'); @@ -367,8 +354,6 @@ proto.writeBytes = function (buf) { * ``` */ proto.writeString = function (str) { - this._assertType('writeString', 'string', str); - var length = str.length; var strOffset = 0; var sublen; diff --git a/package.json b/package.json index 0f101f2..fd1f8a7 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "dependencies": { "byte": "^1.1.6", "debug": "^2.6.8", + "flatstr": "^1.0.5", "is-type-of": "^1.1.0", "long": "^3.2.0", "utility": "^1.12.0" @@ -44,6 +45,7 @@ "autod": "*", "beautify-benchmark": "*", "benchmark": "*", + "fastbench": "^1.0.1", "istanbul": "*", "js-to-java": "2", "jshint": "*", @@ -52,4 +54,4 @@ "engines": { "node": ">= 0.12.0" } -} \ No newline at end of file +} From b5127b85bc872c09f99b676ad9c5bf4c37c5d70c Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Mon, 7 Aug 2017 16:16:29 +0800 Subject: [PATCH 2/2] f --- lib/v2/encoder.js | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/v2/encoder.js b/lib/v2/encoder.js index baed5a9..6b411d6 100644 --- a/lib/v2/encoder.js +++ b/lib/v2/encoder.js @@ -1,7 +1,6 @@ 'use strict'; var debug = require('debug')('hessian:v2:encoder'); -var flatstr = require('flatstr'); var is = require('is-type-of'); var util = require('util'); var EncoderV1 = require('../v1/encoder'); diff --git a/package.json b/package.json index fd1f8a7..92dca5d 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "dependencies": { "byte": "^1.1.6", "debug": "^2.6.8", - "flatstr": "^1.0.5", "is-type-of": "^1.1.0", "long": "^3.2.0", "utility": "^1.12.0"