From 6b2d9397bffb944bb5566153ded03247e82651dc Mon Sep 17 00:00:00 2001 From: Siim Kallas Date: Thu, 18 Aug 2022 21:37:14 +0300 Subject: [PATCH 1/4] perf: improve hexToBase64 --- .../src/platform/node/hex-to-base64.ts | 30 ++++++++++++++----- .../test/platform/hex-to-base64.test.ts | 6 ++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts b/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts index 9330aaa91f..31fd1890c1 100644 --- a/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts +++ b/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts @@ -13,14 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +function intValue(charCode: number): number { + // 0-9 + if (charCode >= 48 && charCode <= 57) { + return charCode - 48; + } + + // a-f + if (charCode >= 97 && charCode <= 102) { + return charCode - 87; + } + + // A-F + return charCode - 55; +} + export function hexToBase64(hexStr: string): string { - const hexStrLen = hexStr.length; - let hexAsciiCharsStr = ''; - for (let i = 0; i < hexStrLen; i += 2) { - const hexPair = hexStr.substring(i, i + 2); - const hexVal = parseInt(hexPair, 16); - hexAsciiCharsStr += String.fromCharCode(hexVal); + const buf = Buffer.alloc(hexStr.length / 2); + let offset = 0; + + for (let i = 0; i < hexStr.length; i += 2) { + const hi = intValue(hexStr.charCodeAt(i)); + const lo = intValue(hexStr.charCodeAt(i + 1)); + buf.writeUInt8((hi << 4) | lo, offset++); } - return Buffer.from(hexAsciiCharsStr, 'ascii').toString('base64'); + return buf.toString('base64'); } diff --git a/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts b/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts index 7f78ded3ef..30346e3656 100644 --- a/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts +++ b/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts @@ -20,8 +20,10 @@ import { hexToBase64 } from '../../src/platform'; describe('hexToBase64', () => { it('convert hex to base64', () => { const id1 = '7deb739e02e44ef2'; - const id2 = '46cef837b919a16ff26e608c8cf42c80'; + const id2 = '12abc034d567e89ff26e608c8cf42c80'; + const id3 = id2.toUpperCase(); assert.strictEqual(hexToBase64(id1), 'fetzngLkTvI='); - assert.strictEqual(hexToBase64(id2), 'Rs74N7kZoW/ybmCMjPQsgA=='); + assert.strictEqual(hexToBase64(id2), 'EqvANNVn6J/ybmCMjPQsgA=='); + assert.strictEqual(hexToBase64(id3), 'EqvANNVn6J/ybmCMjPQsgA=='); }); }); From b54210544e4813e49a1b9b2c8f3358fcf1897068 Mon Sep 17 00:00:00 2001 From: Siim Kallas Date: Thu, 18 Aug 2022 22:19:22 +0300 Subject: [PATCH 2/4] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 741b1d5da3..ebd447e6b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### :rocket: (Enhancement) +* perf(opentelemetry-core): improve hexToBase64 performance [#3178](https://github.com/open-telemetry/opentelemetry-js/pull/3178) * feat(sdk-trace-base): move Sampler declaration into sdk-trace-base [#3088](https://github.com/open-telemetry/opentelemetry-js/pull/3088) @legendecas * fix(grpc-instrumentation): added grpc attributes in instrumentation [#3127](https://github.com/open-telemetry/opentelemetry-js/pull/3127) @andrewzenkov From 95966fa0d47eee3c503e8943472ecf0dd2518ff4 Mon Sep 17 00:00:00 2001 From: Siim Kallas Date: Mon, 22 Aug 2022 16:24:34 +0300 Subject: [PATCH 3/4] perf: hexToBase64: use preallocated buffer --- .../src/platform/node/hex-to-base64.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts b/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts index 31fd1890c1..66d4ad0fe9 100644 --- a/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts +++ b/packages/opentelemetry-core/src/platform/node/hex-to-base64.ts @@ -28,8 +28,18 @@ function intValue(charCode: number): number { return charCode - 55; } +const buf8 = Buffer.alloc(8); +const buf16 = Buffer.alloc(16); + export function hexToBase64(hexStr: string): string { - const buf = Buffer.alloc(hexStr.length / 2); + let buf; + if (hexStr.length === 16) { + buf = buf8; + } else if (hexStr.length === 32) { + buf = buf16; + } else { + buf = Buffer.alloc(hexStr.length / 2); + } let offset = 0; for (let i = 0; i < hexStr.length; i += 2) { From 13471d75cd19829b62d15a41faf802410c105b9f Mon Sep 17 00:00:00 2001 From: Siim Kallas Date: Mon, 22 Aug 2022 17:02:44 +0300 Subject: [PATCH 4/4] test: add a test for longer hex strings --- packages/opentelemetry-core/test/platform/hex-to-base64.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts b/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts index 30346e3656..d73409fc09 100644 --- a/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts +++ b/packages/opentelemetry-core/test/platform/hex-to-base64.test.ts @@ -25,5 +25,7 @@ describe('hexToBase64', () => { assert.strictEqual(hexToBase64(id1), 'fetzngLkTvI='); assert.strictEqual(hexToBase64(id2), 'EqvANNVn6J/ybmCMjPQsgA=='); assert.strictEqual(hexToBase64(id3), 'EqvANNVn6J/ybmCMjPQsgA=='); + // Don't use the preallocated path + assert.strictEqual(hexToBase64(id2.repeat(2)), 'EqvANNVn6J/ybmCMjPQsgBKrwDTVZ+if8m5gjIz0LIA='); }); });