-
Notifications
You must be signed in to change notification settings - Fork 29.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
buffer: speed up swap16/32, add swap64
* 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: #7157 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
- Loading branch information
1 parent
5615487
commit a1059af
Showing
5 changed files
with
351 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.