From a3dee0695db8de07eec4ca6ffad6f46bcb82b499 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 26 Jun 2015 14:02:26 -0400 Subject: [PATCH 1/7] Improved performance of bufferReader.readUInt64LEBN() --- benchmark/serialization.js | 39 ++++++++++++++++++++++++++++++++++++ lib/encoding/bufferreader.js | 7 ++----- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/benchmark/serialization.js b/benchmark/serialization.js index 7b45cc761..d0024bd37 100644 --- a/benchmark/serialization.js +++ b/benchmark/serialization.js @@ -14,6 +14,45 @@ console.log('Benchmarking Block/Transaction Serialization'); console.log('---------------------------------------'); async.series([ + function(next) { + + var buffers = []; + console.log('Generating Random Test Data...'); + for (var i = 0; i < 100; i++) { + var br = new bitcore.encoding.BufferWriter(); + var num = Math.round(Math.random() * 10000000000000); + br.writeUInt64LEBN(new bitcore.crypto.BN(num)); + buffers.push(br.toBuffer()); + } + + var c = 0; + var bn; + + function readUInt64LEBN() { + if (c >= buffers.length) { + c = 0; + } + var buf = buffers[c]; + var br = new bitcore.encoding.BufferReader(buf); + bn = br.readUInt64LEBN(); + c++; + } + + console.log('Starting benchmark...'); + + var suite = new benchmark.Suite(); + suite.add('bufferReader.readUInt64LEBN()', readUInt64LEBN, {maxTime: maxTime}); + suite + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('complete', function() { + console.log('Done'); + console.log('----------------------------------------------------------------------'); + next(); + }) + .run(); + }, function(next) { var block1; diff --git a/lib/encoding/bufferreader.js b/lib/encoding/bufferreader.js index bb881fb76..19c6f818b 100644 --- a/lib/encoding/bufferreader.js +++ b/lib/encoding/bufferreader.js @@ -91,11 +91,8 @@ BufferReader.prototype.readUInt64BEBN = function() { }; BufferReader.prototype.readUInt64LEBN = function() { - var buf = this.buf.slice(this.pos, this.pos + 8); - var reversebuf = BufferReader({ - buf: buf - }).readReverse(); - var bn = BN.fromBuffer(reversebuf); + var data = this.buf.slice(this.pos, this.pos + 8).toJSON().data; + var bn = new BN(data, 10, 'le'); this.pos = this.pos + 8; return bn; }; From 8e6b0147b94b9716f4b37bee2b4da076f8fcb290 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 26 Jun 2015 16:38:42 -0400 Subject: [PATCH 2/7] Fixed issue with incompatible buffer.toJSON for node 0.10 and 0.12 --- lib/encoding/bufferreader.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/encoding/bufferreader.js b/lib/encoding/bufferreader.js index 19c6f818b..34f8580bf 100644 --- a/lib/encoding/bufferreader.js +++ b/lib/encoding/bufferreader.js @@ -91,8 +91,9 @@ BufferReader.prototype.readUInt64BEBN = function() { }; BufferReader.prototype.readUInt64LEBN = function() { - var data = this.buf.slice(this.pos, this.pos + 8).toJSON().data; - var bn = new BN(data, 10, 'le'); + var buf = this.buf.slice(this.pos, this.pos + 8); + var array = Array.prototype.slice.call(buf, 0); + var bn = new BN(array, 10, 'le'); this.pos = this.pos + 8; return bn; }; From e74a65fd0a98d15fec3687b0c33871bf2e7f79fd Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 26 Jun 2015 19:21:15 -0400 Subject: [PATCH 3/7] Combined slice calls for performance improvement. --- lib/encoding/bufferreader.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/encoding/bufferreader.js b/lib/encoding/bufferreader.js index 34f8580bf..a730a61dd 100644 --- a/lib/encoding/bufferreader.js +++ b/lib/encoding/bufferreader.js @@ -91,9 +91,8 @@ BufferReader.prototype.readUInt64BEBN = function() { }; BufferReader.prototype.readUInt64LEBN = function() { - var buf = this.buf.slice(this.pos, this.pos + 8); - var array = Array.prototype.slice.call(buf, 0); - var bn = new BN(array, 10, 'le'); + var data = Array.prototype.slice.call(this.buf, this.pos, this.pos + 8); + var bn = new BN(data, 10, 'le'); this.pos = this.pos + 8; return bn; }; From 805182c50004e16a1c3f4048b25c0286532da497 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 29 Jun 2015 21:43:11 -0400 Subject: [PATCH 4/7] Removed readReverse() precondition for a 2x block parsing improvement. --- benchmark/serialization.js | 20 ++++++++++++++++++++ lib/util/buffer.js | 1 - test/util/buffer.js | 5 ----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/benchmark/serialization.js b/benchmark/serialization.js index d0024bd37..ee948a60e 100644 --- a/benchmark/serialization.js +++ b/benchmark/serialization.js @@ -17,12 +17,19 @@ async.series([ function(next) { var buffers = []; + var hashBuffers = []; console.log('Generating Random Test Data...'); for (var i = 0; i < 100; i++) { + + // uint64le var br = new bitcore.encoding.BufferWriter(); var num = Math.round(Math.random() * 10000000000000); br.writeUInt64LEBN(new bitcore.crypto.BN(num)); buffers.push(br.toBuffer()); + + // hashes + var data = bitcore.crypto.Hash.sha256sha256(new Buffer(32)); + hashBuffers.push(data); } var c = 0; @@ -38,10 +45,23 @@ async.series([ c++; } + var reversed; + + function readReverse() { + if (c >= hashBuffers.length) { + c = 0; + } + var buf = hashBuffers[c]; + var br = new bitcore.encoding.BufferReader(buf); + reversed = br.readReverse(); + c++; + } + console.log('Starting benchmark...'); var suite = new benchmark.Suite(); suite.add('bufferReader.readUInt64LEBN()', readUInt64LEBN, {maxTime: maxTime}); + suite.add('bufferReader.readReverse()', readReverse, {maxTime: maxTime}); suite .on('cycle', function(event) { console.log(String(event.target)); diff --git a/lib/util/buffer.js b/lib/util/buffer.js index 332b4be80..c78b9396e 100644 --- a/lib/util/buffer.js +++ b/lib/util/buffer.js @@ -152,7 +152,6 @@ module.exports = { * @return {Buffer} */ reverse: function reverse(param) { - $.checkArgumentType(param, 'Buffer', 'param'); var ret = new buffer.Buffer(param.length); for (var i = 0; i < param.length; i++) { ret[i] = param[param.length - i - 1]; diff --git a/test/util/buffer.js b/test/util/buffer.js index 04e058270..bbc1e2609 100644 --- a/test/util/buffer.js +++ b/test/util/buffer.js @@ -152,10 +152,5 @@ describe('buffer utils', function() { original[1].should.equal(reversed[1]); original[2].should.equal(reversed[0]); }); - it('checks the argument type', function() { - expect(function() { - BufferUtil.reverse('invalid'); - }).to.throw(errors.InvalidArgumentType); - }); }); }); From 08a80d74d515737322516fb55151083752f26c25 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 29 Jun 2015 23:51:03 -0400 Subject: [PATCH 5/7] more optimizations for readUInt64lebn --- lib/encoding/bufferreader.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/encoding/bufferreader.js b/lib/encoding/bufferreader.js index a730a61dd..73f351daf 100644 --- a/lib/encoding/bufferreader.js +++ b/lib/encoding/bufferreader.js @@ -91,8 +91,16 @@ BufferReader.prototype.readUInt64BEBN = function() { }; BufferReader.prototype.readUInt64LEBN = function() { - var data = Array.prototype.slice.call(this.buf, this.pos, this.pos + 8); - var bn = new BN(data, 10, 'le'); + var second = this.buf.readUInt32LE(this.pos); + var first = this.buf.readUInt32LE(this.pos + 4); + var combined = (first * 0x100000000) + second; + var bn; + if (combined <= 0x1fffffffffffff) { + bn = new BN(combined); + } else { + var data = Array.prototype.slice.call(this.buf, this.pos, this.pos + 8); + bn = new BN(data, 10, 'le'); + } this.pos = this.pos + 8; return bn; }; From 2a7186399258bd90679ecd9ce0bd072fcb8a7d97 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 30 Jun 2015 16:19:53 -0400 Subject: [PATCH 6/7] Added additional test cases for readUInt64LEBN --- test/encoding/bufferreader.js | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/encoding/bufferreader.js b/test/encoding/bufferreader.js index e3b66c146..78e4c764c 100644 --- a/test/encoding/bufferreader.js +++ b/test/encoding/bufferreader.js @@ -190,6 +190,13 @@ describe('BufferReader', function() { br.readUInt64LEBN().toNumber().should.equal(1); }); + it('should return 10BTC', function() { + var tenbtc = 10 * 1e8; + var tenbtcBuffer = new Buffer('00ca9a3b00000000', 'hex'); + var br = new BufferReader(tenbtcBuffer); + br.readUInt64LEBN().toNumber().should.equal(tenbtc); + }); + it('should return 2^30', function() { var buf = new Buffer(8); buf.fill(0); @@ -198,6 +205,35 @@ describe('BufferReader', function() { br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 30)); }); + it('should return 2^32 + 1', function() { + var num = Math.pow(2, 32) + 1; + var numBuffer = new Buffer('0100000001000000', 'hex'); + var br = new BufferReader(numBuffer); + br.readUInt64LEBN().toNumber().should.equal(num); + }); + + it('should return max number of satoshis', function() { + var maxSatoshis = 21000000 * 1e8; + var maxSatoshisBuffer = new Buffer('0040075af0750700', 'hex'); + var br = new BufferReader(maxSatoshisBuffer); + br.readUInt64LEBN().toNumber().should.equal(maxSatoshis); + }); + + it('should return 2^53 - 1', function() { + var maxSafe = Math.pow(2, 53) - 1; + var maxSafeBuffer = new Buffer('ffffffffffff1f00', 'hex'); + var br = new BufferReader(maxSafeBuffer); + br.readUInt64LEBN().toNumber().should.equal(maxSafe); + }); + + it('should return 2^53', function() { + var bn = new BN('20000000000000', 16); + var bnBuffer = new Buffer('0000000000002000', 'hex'); + var br = new BufferReader(bnBuffer); + var readbn = br.readUInt64LEBN(); + readbn.cmp(bn).should.equal(0); + }); + it('should return 0', function() { var buf = new Buffer(8); buf.fill(0); From 4a681f967e2bc673fb7270a6367ffc03e3839b73 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Wed, 1 Jul 2015 15:30:21 -0400 Subject: [PATCH 7/7] Added comment with reasoning for number or array BN instantiation. --- lib/encoding/bufferreader.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/encoding/bufferreader.js b/lib/encoding/bufferreader.js index 73f351daf..d7ca1575c 100644 --- a/lib/encoding/bufferreader.js +++ b/lib/encoding/bufferreader.js @@ -94,6 +94,12 @@ BufferReader.prototype.readUInt64LEBN = function() { var second = this.buf.readUInt32LE(this.pos); var first = this.buf.readUInt32LE(this.pos + 4); var combined = (first * 0x100000000) + second; + // Instantiating an instance of BN with a number is faster than with an + // array or string. However, the maximum safe number for a double precision + // floating point is 2 ^ 52 - 1 (0x1fffffffffffff), thus we can safely use + // non-floating point numbers less than this amount (52 bits). And in the case + // that the number is larger, we can instatiate an instance of BN by passing + // an array from the buffer (slower) and specifying the endianness. var bn; if (combined <= 0x1fffffffffffff) { bn = new BN(combined);