From 61fd2e87d743882aa9afe18ec2685b7ad0b95f21 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 5 May 2024 20:22:25 +0900 Subject: [PATCH 1/9] websocket: improve performance of generate mask --- benchmarks/websocket/generate-mask.mjs | 22 +++++++++++++++++ lib/web/websocket/frame.js | 33 +++++++++++++++++++------- 2 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 benchmarks/websocket/generate-mask.mjs diff --git a/benchmarks/websocket/generate-mask.mjs b/benchmarks/websocket/generate-mask.mjs new file mode 100644 index 00000000000..bf145ee366d --- /dev/null +++ b/benchmarks/websocket/generate-mask.mjs @@ -0,0 +1,22 @@ +import { randomFillSync, randomBytes } from 'node:crypto' +import { bench, group, run } from 'mitata' + +const BUFFER_SIZE = 16384 + +const buf = randomFillSync(Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE) +let bufIdx = 0 + +function generateMask () { + if (bufIdx + 4 > BUFFER_SIZE) { + bufIdx = 0 + randomFillSync(buf, 0, BUFFER_SIZE) + } + return [buf[bufIdx++], buf[bufIdx++], buf[bufIdx++], buf[bufIdx++]] +} + +group('generate', () => { + bench('generateMask', () => generateMask()) + bench('crypto.randomBytes(4)', () => randomBytes(4)) +}) + +await run() diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index 30c68c811cd..95cf7086c13 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -2,8 +2,13 @@ const { maxUnsigned16Bit } = require('./constants') +const BUFFER_SIZE = 16386 + /** @type {import('crypto')} */ let crypto +let buffer = null +let bufIdx = 0 + try { crypto = require('node:crypto') /* c8 ignore next 3 */ @@ -11,17 +16,29 @@ try { } +function generateMask () { + if (buffer === null) { + buffer = crypto.randomFillSync(Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE) + } + if (bufIdx + 4 > BUFFER_SIZE) { + bufIdx = 0 + crypto.randomFillSync(buffer, 0, BUFFER_SIZE) + } + return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]] +} + class WebsocketFrameSend { /** * @param {Buffer|undefined} data */ constructor (data) { this.frameData = data - this.maskKey = crypto.randomBytes(4) } createFrame (opcode) { - const bodyLength = this.frameData?.byteLength ?? 0 + const frameData = this.frameData + const maskKey = generateMask() + const bodyLength = frameData?.byteLength ?? 0 /** @type {number} */ let payloadLength = bodyLength // 0-125 @@ -43,10 +60,10 @@ class WebsocketFrameSend { buffer[0] = (buffer[0] & 0xF0) + opcode // opcode /*! ws. MIT License. Einar Otto Stangvik */ - buffer[offset - 4] = this.maskKey[0] - buffer[offset - 3] = this.maskKey[1] - buffer[offset - 2] = this.maskKey[2] - buffer[offset - 1] = this.maskKey[3] + buffer[offset - 4] = maskKey[0] + buffer[offset - 3] = maskKey[1] + buffer[offset - 2] = maskKey[2] + buffer[offset - 1] = maskKey[3] buffer[1] = payloadLength @@ -61,8 +78,8 @@ class WebsocketFrameSend { buffer[1] |= 0x80 // MASK // mask body - for (let i = 0; i < bodyLength; i++) { - buffer[offset + i] = this.frameData[i] ^ this.maskKey[i % 4] + for (let i = 0; i < bodyLength; ++i) { + buffer[offset + i] = frameData[i] ^ maskKey[i & 3] } return buffer From 6b588b0ee9a6df9d8eb785c4d9cc19f83d0539ab Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 5 May 2024 21:57:47 +0900 Subject: [PATCH 2/9] apply comments --- benchmarks/websocket/generate-mask.mjs | 6 +++--- lib/web/websocket/frame.js | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/benchmarks/websocket/generate-mask.mjs b/benchmarks/websocket/generate-mask.mjs index bf145ee366d..032f05d8b99 100644 --- a/benchmarks/websocket/generate-mask.mjs +++ b/benchmarks/websocket/generate-mask.mjs @@ -3,11 +3,11 @@ import { bench, group, run } from 'mitata' const BUFFER_SIZE = 16384 -const buf = randomFillSync(Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE) -let bufIdx = 0 +const buf = Buffer.allocUnsafe(BUFFER_SIZE) +let bufIdx = BUFFER_SIZE function generateMask () { - if (bufIdx + 4 > BUFFER_SIZE) { + if (bufIdx === BUFFER_SIZE) { bufIdx = 0 randomFillSync(buf, 0, BUFFER_SIZE) } diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index 95cf7086c13..46723f6623e 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -6,8 +6,8 @@ const BUFFER_SIZE = 16386 /** @type {import('crypto')} */ let crypto -let buffer = null -let bufIdx = 0 +let buffer = Buffer.allocUnsafe(BUFFER_SIZE) +let bufIdx = BUFFER_SIZE try { crypto = require('node:crypto') @@ -17,10 +17,7 @@ try { } function generateMask () { - if (buffer === null) { - buffer = crypto.randomFillSync(Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE) - } - if (bufIdx + 4 > BUFFER_SIZE) { + if (bufIdx === BUFFER_SIZE) { bufIdx = 0 crypto.randomFillSync(buffer, 0, BUFFER_SIZE) } From 5e27ed2181684046befb35911d79bbcf524ff7d2 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Sun, 5 May 2024 21:59:33 +0900 Subject: [PATCH 3/9] Apply suggestions from code review --- lib/web/websocket/frame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index 46723f6623e..c901912fdf8 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -6,7 +6,7 @@ const BUFFER_SIZE = 16386 /** @type {import('crypto')} */ let crypto -let buffer = Buffer.allocUnsafe(BUFFER_SIZE) +const buffer = Buffer.allocUnsafe(BUFFER_SIZE) let bufIdx = BUFFER_SIZE try { From 277bf41b03654723255e7814e2593994f0b2afea Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Mon, 6 May 2024 05:52:38 +0900 Subject: [PATCH 4/9] Apply suggestions from code review --- lib/web/websocket/frame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index c901912fdf8..b8ea8b11d34 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -19,7 +19,7 @@ try { function generateMask () { if (bufIdx === BUFFER_SIZE) { bufIdx = 0 - crypto.randomFillSync(buffer, 0, BUFFER_SIZE) + crypto.randomFillSync((buffer ??= Buffer.allocUnsafe(BUFFER_SIZE)), 0, BUFFER_SIZE) } return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]] } From 7def4f4c8bf179be681c0a283dc1e59f53cfa3ed Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Mon, 6 May 2024 05:52:54 +0900 Subject: [PATCH 5/9] Apply suggestions from code review Co-authored-by: Aras Abbasi --- lib/web/websocket/frame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index b8ea8b11d34..fe3a1c6238a 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -6,7 +6,7 @@ const BUFFER_SIZE = 16386 /** @type {import('crypto')} */ let crypto -const buffer = Buffer.allocUnsafe(BUFFER_SIZE) +const buffer = null let bufIdx = BUFFER_SIZE try { From afb2f8b17081223a79add42eae22c75b1a68a387 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Mon, 6 May 2024 05:55:20 +0900 Subject: [PATCH 6/9] Apply suggestions from code review --- lib/web/websocket/frame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index fe3a1c6238a..edfd7f50521 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -6,7 +6,7 @@ const BUFFER_SIZE = 16386 /** @type {import('crypto')} */ let crypto -const buffer = null +let buffer = null let bufIdx = BUFFER_SIZE try { From efa1de5f53e2ed0ffb9b98b46eade165fc5941dd Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Tue, 7 May 2024 19:03:22 +0900 Subject: [PATCH 7/9] Update lib/web/websocket/frame.js --- lib/web/websocket/frame.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index edfd7f50521..803d9692af0 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -13,7 +13,14 @@ try { crypto = require('node:crypto') /* c8 ignore next 3 */ } catch { - + crypto = { + // not full compatibility, but minimum. + randomFillSync: function randomFillSync (buffer, _offset, _size) { + for (let i = 0; i < buffer.length; ++i) { + buffer[i] = Math.floor(Math.random() * 255) + } + } + } } function generateMask () { From 56b69b52ac4f7c84d297bbe29dcac933d779252b Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Tue, 7 May 2024 19:04:50 +0900 Subject: [PATCH 8/9] Update lib/web/websocket/frame.js --- lib/web/websocket/frame.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index 803d9692af0..199dc2cb11c 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -19,6 +19,7 @@ try { for (let i = 0; i < buffer.length; ++i) { buffer[i] = Math.floor(Math.random() * 255) } + return buffer } } } From 720e8d7ced58700848d48999473fdcdb4081e350 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Tue, 7 May 2024 19:20:13 +0900 Subject: [PATCH 9/9] Update lib/web/websocket/frame.js Co-authored-by: Aras Abbasi --- lib/web/websocket/frame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/websocket/frame.js b/lib/web/websocket/frame.js index 199dc2cb11c..b062ffde8ec 100644 --- a/lib/web/websocket/frame.js +++ b/lib/web/websocket/frame.js @@ -17,7 +17,7 @@ try { // not full compatibility, but minimum. randomFillSync: function randomFillSync (buffer, _offset, _size) { for (let i = 0; i < buffer.length; ++i) { - buffer[i] = Math.floor(Math.random() * 255) + buffer[i] = Math.random() * 255 | 0 } return buffer }