-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
Closed
Labels
bufferIssues and PRs related to the buffer subsystem.Issues and PRs related to the buffer subsystem.
Description
The write
family of functions on Buffer
objects (buf.writeUInt8
etc.) have a lot of overhead when noAssert
isn't set to true -- much more than I would normally expect from a bounds check.
Here are some timings I got for filling a 100MB buffer (Node 7.5.0 on 64-bit Linux):
Assigning buf[i], buf[i+1]: 392.496ms
buf.writeUInt16LE, noAssert: 342.778ms
buf.writeUInt16LE, without noAssert: 9072.283ms
Assigning buf[i]: 288.415ms
buf.writeUInt8, noAssert: 312.455ms
buf.writeUInt8, without noAssert: 8725.956ms
I had a lot of timing variation, so I don't think the indexed-vs-writeUInt8 differences are meaningful. This might be because we're down to about 10 CPU cycles per iteration. My bug report is about the unexpected order-of-magnitude difference when noAssert
is not enabled.
Benchmark code:
let count = 100000000
let buf1, buf2
function timeAssign16() {
let buf1
buf1 = Buffer.allocUnsafe(count * 2)
console.time('Assigning buf[i], buf[i+1]')
for (let i = 0; i < count; i++) {
let uint16 = i % 2**16
buf1[i*2] = uint16 % 256
buf1[i*2 + 1] = (uint16 >> 8) % 256
}
console.timeEnd('Assigning buf[i], buf[i+1]')
return buf1
}
function timeWrite16() {
let buf2
buf2 = Buffer.allocUnsafe(count * 2)
console.time('buf.writeUInt16LE, noAssert')
for (let i = 0; i < count; i++) {
buf2.writeUInt16LE(i % 2**16, i * 2, true)
}
console.timeEnd('buf.writeUInt16LE, noAssert')
return buf2
}
function timeWrite16Checked() {
let buf2
buf2 = Buffer.allocUnsafe(count * 2)
console.time('buf.writeUInt16LE, without noAssert')
for (let i = 0; i < count; i++) {
buf2.writeUInt16LE(i % 2**16, i * 2)
}
console.timeEnd('buf.writeUInt16LE, without noAssert')
return buf2
}
buf1 = timeAssign16()
buf2 = timeWrite16()
buf2 = timeWrite16Checked()
if (buf1.compare(buf2) !== 0) throw 'error'
console.log()
function timeAssign8() {
let buf1
buf1 = Buffer.allocUnsafe(count)
console.time('Assigning buf[i]')
for (let i = 0; i < count; i++) {
buf1[i] = i % 256
}
console.timeEnd('Assigning buf[i]')
return buf1
}
function timeWrite8() {
let buf2
buf2 = Buffer.allocUnsafe(count)
console.time('buf.writeUInt8, noAssert')
for (let i = 0; i < count; i++) {
buf2.writeUInt8(i % 256, i, true)
}
console.timeEnd('buf.writeUInt8, noAssert')
return buf2
}
function timeWrite8Checked() {
let buf2
buf2 = Buffer.allocUnsafe(count)
console.time('buf.writeUInt8, without noAssert')
for (let i = 0; i < count; i++) {
buf2.writeUInt8(i % 256, i)
}
console.timeEnd('buf.writeUInt8, without noAssert')
return buf2
}
buf1 = timeAssign8()
buf2 = timeWrite8()
buf2 = timeWrite8Checked()
if (buf1.compare(buf2) !== 0) throw 'error'
Related: #11244 ("Unclear if Buffer buf[i] is bounds-checked"). If buf[i]
is indeed bounds-checked, then surely we should be able to achieve the same performance with noAssert = false
?
Metadata
Metadata
Assignees
Labels
bufferIssues and PRs related to the buffer subsystem.Issues and PRs related to the buffer subsystem.