-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
buffer: up to 2x times faster copy of buffers #24977
Changes from all commits
0152f4a
655a9a5
15dfef3
0db3b64
158e584
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
'use strict'; | ||
const common = require('../common.js'); | ||
const { randomBytes } = require('crypto'); | ||
|
||
const KB = 1024; | ||
|
||
const bench = common.createBenchmark(main, { | ||
size: [10, KB, 2 * KB, 8 * KB, 64 * KB /* , 256 * KB */], | ||
// This option checks edge case when target.length !== source.length. | ||
targetStart: [0, 1], | ||
n: [1024] | ||
}); | ||
|
||
function main({ n, size, targetStart }) { | ||
const source = randomBytes(size); | ||
const target = randomBytes(size); | ||
|
||
bench.start(); | ||
for (var i = 0; i < n * 1024; i++) { | ||
source.copy(target, targetStart); | ||
} | ||
bench.end(n); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,8 +22,8 @@ | |
'use strict'; | ||
|
||
const { | ||
byteLengthUtf8, | ||
copy: _copy, | ||
byteLengthUtf8, | ||
compare: _compare, | ||
compareOffset, | ||
createFromString, | ||
|
@@ -401,7 +401,7 @@ function fromObject(obj) { | |
if (b.length === 0) | ||
return b; | ||
|
||
_copy(obj, b, 0, 0, obj.length); | ||
fastcopy(obj, b, 0, 0, obj.length); | ||
return b; | ||
} | ||
|
||
|
@@ -473,7 +473,7 @@ Buffer.concat = function concat(list, length) { | |
throw new ERR_INVALID_ARG_TYPE( | ||
`list[${i}]`, ['Array', 'Buffer', 'Uint8Array'], list[i]); | ||
} | ||
_copy(buf, buffer, pos); | ||
fastcopy(buf, buffer, pos); | ||
pos += buf.length; | ||
} | ||
|
||
|
@@ -488,6 +488,59 @@ Buffer.concat = function concat(list, length) { | |
return buffer; | ||
}; | ||
|
||
function fastcopy(source, target, targetStart = 0, sourceStart = 0, sourceEnd) { | ||
if (!isUint8Array(source)) { | ||
throw new ERR_INVALID_ARG_TYPE('source', ['Buffer', 'Uint8Array'], source); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is already checked in the |
||
|
||
if (!isUint8Array(target)) { | ||
throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'Uint8Array'], target); | ||
} | ||
|
||
if (targetStart < 0) | ||
throw new ERR_OUT_OF_RANGE('targetStart', '>= 0', targetStart); | ||
targetStart >>>= 0; | ||
|
||
if (sourceStart < 0) | ||
throw new ERR_OUT_OF_RANGE('sourceStart', '>= 0', sourceStart); | ||
sourceStart >>>= 0; | ||
|
||
if (sourceEnd === undefined) | ||
sourceEnd = source.byteLength; | ||
else if (sourceEnd < 0) | ||
throw new ERR_OUT_OF_RANGE('sourceEnd', '>= 0', sourceEnd); | ||
else | ||
sourceEnd >>>= 0; | ||
|
||
if (targetStart >= target.byteLength || sourceStart >= sourceEnd) | ||
return 0; | ||
|
||
if (sourceStart > source.byteLength) { | ||
throw new ERR_OUT_OF_RANGE( | ||
'sourceStart', `<= ${source.byteLength}`, sourceStart | ||
); | ||
} | ||
|
||
const targetBytesAmount = target.byteLength - targetStart; | ||
|
||
if (sourceEnd - sourceStart > targetBytesAmount) | ||
sourceEnd = sourceStart + targetBytesAmount; | ||
|
||
const bytesAmount = Math.min( | ||
sourceEnd - sourceStart, | ||
targetBytesAmount, | ||
source.byteLength - sourceStart | ||
); | ||
|
||
if (sourceStart > 0 || bytesAmount < source.byteLength) { | ||
sourceEnd = sourceStart + bytesAmount; | ||
return _copy(source, target, targetStart, sourceStart, sourceEnd); | ||
} | ||
|
||
target.set(source, targetStart); | ||
return bytesAmount; | ||
} | ||
|
||
function base64ByteLength(str, bytes) { | ||
// Handle padding | ||
if (str.charCodeAt(bytes - 1) === 0x3D) | ||
|
@@ -626,7 +679,7 @@ function stringSlice(buf, encoding, start, end) { | |
|
||
Buffer.prototype.copy = | ||
function copy(target, targetStart, sourceStart, sourceEnd) { | ||
return _copy(this, target, targetStart, sourceStart, sourceEnd); | ||
return fastcopy(this, target, targetStart, sourceStart, sourceEnd); | ||
}; | ||
|
||
// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -964,7 +964,8 @@ common.expectsError( | |||
{ | ||||
code: 'ERR_INVALID_ARG_TYPE', | ||||
type: TypeError, | ||||
message: 'argument must be a buffer' | ||||
message: 'The "target" argument must be one of type Buffer or ' + | ||||
'Uint8Array. Received type undefined' | ||||
}); | ||||
|
||||
assert.throws(() => Buffer.from(), { | ||||
|
@@ -1009,13 +1010,6 @@ assert.strictEqual(SlowBuffer.prototype.offset, undefined); | |||
assert.throws(() => Buffer.from(new ArrayBuffer(0), -1 >>> 0), errMsg); | ||||
} | ||||
|
||||
// ParseArrayIndex() should reject values that don't fit in a 32 bits size_t. | ||||
common.expectsError(() => { | ||||
const a = Buffer.alloc(1); | ||||
const b = Buffer.alloc(1); | ||||
a.copy(b, 0, 0x100000000, 0x100000001); | ||||
}, outOfRangeError); | ||||
|
||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just because it's not obvious why the test is removed: can you elaborate why it's now obsolete? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Line 164 in afeb56a
|
||||
// Unpooled buffer (replaces SlowBuffer) | ||||
{ | ||||
const ubuf = Buffer.allocUnsafeSlow(10); | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously we allowed any
TypedArray
(e.g.Uint16Array
) as well asDataView
as the source and target. Please also make sure to add tests for these cases.