mirror of https://github.com/lukechilds/node.git
Browse Source
* Speed up buffer.swap16 and swap32 by using builtins. Up to ~6x gain. Drop transition point between JS and C++ implementations accordingly. Amount of performance improvement not only depends on buffer size but also memory alignment. * Fix tests: C++ impl tests were testing 0-filled buffers so were always passing. * Add similar buffer.swap64 method. * Make buffer-swap benchmark mirror JS impl. doc/api/buffer.markdown has an entry of "added: REPLACEME" that should be changed to the correct release number before tagged. Because node is currently using a very old version of cpplint.py it doesn't know that std::swap() has moved from <algorithm> to <utility> in c++11. So until cpplint.py is updated simply NOLINT the line. Technically it should be NOLINT(build/include_what_you_use), but that puts the line over 80 characters causing another lint error. PR-URL: https://github.com/nodejs/node/pull/7157 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>v7.x
committed by
Trevor Norris
5 changed files with 351 additions and 111 deletions
@ -1,61 +1,90 @@ |
|||||
'use strict'; |
'use strict'; |
||||
|
|
||||
const common = require('../common.js'); |
const common = require('../common.js'); |
||||
|
const v8 = require('v8'); |
||||
|
|
||||
const bench = common.createBenchmark(main, { |
const bench = common.createBenchmark(main, { |
||||
method: ['swap16', 'swap32', 'htons', 'htonl'], |
aligned: ['true', 'false'], |
||||
len: [4, 64, 512, 768, 1024, 1536, 2056, 4096, 8192], |
method: ['swap16', 'swap32', 'swap64'/*, 'htons', 'htonl', 'htonll'*/], |
||||
n: [1e6] |
len: [8, 64, 128, 256, 512, 768, 1024, 1536, 2056, 4096, 8192], |
||||
|
n: [5e7] |
||||
}); |
}); |
||||
|
|
||||
// The htons and htonl methods below are used to benchmark the
|
// The htons and htonl methods below are used to benchmark the
|
||||
// performance difference between doing the byteswap in pure
|
// performance difference between doing the byteswap in pure
|
||||
// javascript regardless of Buffer size as opposed to dropping
|
// javascript regardless of Buffer size as opposed to dropping
|
||||
// down to the native layer for larger Buffer sizes.
|
// down to the native layer for larger Buffer sizes. Commented
|
||||
|
// out by default because they are slow for big buffers. If
|
||||
|
// re-evaluating the crossover point, uncomment those methods
|
||||
|
// and comment out their implementations in lib/buffer.js so
|
||||
|
// C++ version will always be used.
|
||||
|
|
||||
|
function swap(b, n, m) { |
||||
|
const i = b[n]; |
||||
|
b[n] = b[m]; |
||||
|
b[m] = i; |
||||
|
} |
||||
|
|
||||
Buffer.prototype.htons = function htons() { |
Buffer.prototype.htons = function htons() { |
||||
if (this.length % 2 !== 0) |
if (this.length % 2 !== 0) |
||||
throw new RangeError(); |
throw new RangeError(); |
||||
for (var i = 0, n = 0; i < this.length; i += 2) { |
for (var i = 0; i < this.length; i += 2) { |
||||
n = this[i]; |
swap(this, i, i + 1); |
||||
this[i] = this[i + 1]; |
|
||||
this[i + 1] = n; |
|
||||
} |
} |
||||
return this; |
return this; |
||||
}; |
}; |
||||
|
|
||||
Buffer.prototype.htonl = function htonl() { |
Buffer.prototype.htonl = function htonl() { |
||||
if (this.length % 2 !== 0) |
if (this.length % 4 !== 0) |
||||
|
throw new RangeError(); |
||||
|
for (var i = 0; i < this.length; i += 4) { |
||||
|
swap(this, i, i + 3); |
||||
|
swap(this, i + 1, i + 2); |
||||
|
} |
||||
|
return this; |
||||
|
}; |
||||
|
|
||||
|
Buffer.prototype.htonll = function htonl() { |
||||
|
if (this.length % 8 !== 0) |
||||
throw new RangeError(); |
throw new RangeError(); |
||||
for (var i = 0, n = 0; i < this.length; i += 4) { |
for (var i = 0; i < this.length; i += 8) { |
||||
n = this[i]; |
swap(this, i, i + 7); |
||||
this[i] = this[i + 3]; |
swap(this, i + 1, i + 6); |
||||
this[i + 3] = n; |
swap(this, i + 2, i + 5); |
||||
n = this[i + 1]; |
swap(this, i + 3, i + 4); |
||||
this[i + 1] = this[i + 2]; |
|
||||
this[i + 2] = n; |
|
||||
} |
} |
||||
return this; |
return this; |
||||
}; |
}; |
||||
|
|
||||
function createBuffer(len) { |
function createBuffer(len, aligned) { |
||||
|
len += aligned ? 0 : 1; |
||||
const buf = Buffer.allocUnsafe(len); |
const buf = Buffer.allocUnsafe(len); |
||||
for (var i = 1; i <= len; i++) |
for (var i = 1; i <= len; i++) |
||||
buf[i - 1] = i; |
buf[i - 1] = i; |
||||
return buf; |
return aligned ? buf : buf.slice(1); |
||||
} |
} |
||||
|
|
||||
function bufferSwap(n, buf, method) { |
function genMethod(method) { |
||||
for (var i = 1; i <= n; i++) |
const fnString = |
||||
buf[method](); |
'return function ' + method + '(n, buf) {' + |
||||
|
' for (var i = 0; i <= n; i++)' + |
||||
|
' buf.' + method + '();' + |
||||
|
'}'; |
||||
|
return (new Function(fnString))(); |
||||
} |
} |
||||
|
|
||||
function main(conf) { |
function main(conf) { |
||||
const method = conf.method; |
const method = conf.method; |
||||
const len = conf.len | 0; |
const len = conf.len | 0; |
||||
const n = conf.n | 0; |
const n = conf.n | 0; |
||||
const buf = createBuffer(len); |
const aligned = conf.aligned || 'true'; |
||||
|
const buf = createBuffer(len, aligned === 'true'); |
||||
|
const bufferSwap = genMethod(method); |
||||
|
|
||||
|
v8.setFlagsFromString('--allow_natives_syntax'); |
||||
|
eval('%OptimizeFunctionOnNextCall(bufferSwap)'); |
||||
|
|
||||
bench.start(); |
bench.start(); |
||||
bufferSwap(n, buf, method); |
bufferSwap(n, buf); |
||||
bench.end(n); |
bench.end(n); |
||||
} |
} |
||||
|
Loading…
Reference in new issue