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'; |
|||
|
|||
const common = require('../common.js'); |
|||
const v8 = require('v8'); |
|||
|
|||
const bench = common.createBenchmark(main, { |
|||
method: ['swap16', 'swap32', 'htons', 'htonl'], |
|||
len: [4, 64, 512, 768, 1024, 1536, 2056, 4096, 8192], |
|||
n: [1e6] |
|||
aligned: ['true', 'false'], |
|||
method: ['swap16', 'swap32', 'swap64'/*, 'htons', 'htonl', 'htonll'*/], |
|||
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
|
|||
// performance difference between doing the byteswap in pure
|
|||
// 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() { |
|||
if (this.length % 2 !== 0) |
|||
throw new RangeError(); |
|||
for (var i = 0, n = 0; i < this.length; i += 2) { |
|||
n = this[i]; |
|||
this[i] = this[i + 1]; |
|||
this[i + 1] = n; |
|||
for (var i = 0; i < this.length; i += 2) { |
|||
swap(this, i, i + 1); |
|||
} |
|||
return this; |
|||
}; |
|||
|
|||
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(); |
|||
for (var i = 0, n = 0; i < this.length; i += 4) { |
|||
n = this[i]; |
|||
this[i] = this[i + 3]; |
|||
this[i + 3] = n; |
|||
n = this[i + 1]; |
|||
this[i + 1] = this[i + 2]; |
|||
this[i + 2] = n; |
|||
for (var i = 0; i < this.length; i += 8) { |
|||
swap(this, i, i + 7); |
|||
swap(this, i + 1, i + 6); |
|||
swap(this, i + 2, i + 5); |
|||
swap(this, i + 3, i + 4); |
|||
} |
|||
return this; |
|||
}; |
|||
|
|||
function createBuffer(len) { |
|||
function createBuffer(len, aligned) { |
|||
len += aligned ? 0 : 1; |
|||
const buf = Buffer.allocUnsafe(len); |
|||
for (var i = 1; i <= len; i++) |
|||
buf[i - 1] = i; |
|||
return buf; |
|||
return aligned ? buf : buf.slice(1); |
|||
} |
|||
|
|||
function bufferSwap(n, buf, method) { |
|||
for (var i = 1; i <= n; i++) |
|||
buf[method](); |
|||
function genMethod(method) { |
|||
const fnString = |
|||
'return function ' + method + '(n, buf) {' + |
|||
' for (var i = 0; i <= n; i++)' + |
|||
' buf.' + method + '();' + |
|||
'}'; |
|||
return (new Function(fnString))(); |
|||
} |
|||
|
|||
function main(conf) { |
|||
const method = conf.method; |
|||
const len = conf.len | 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(); |
|||
bufferSwap(n, buf, method); |
|||
bufferSwap(n, buf); |
|||
bench.end(n); |
|||
} |
|||
|
Loading…
Reference in new issue