diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 10ed3f40824..cd939478543 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -16,6 +16,8 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 ### :house: Internal +* perf(otlp-transformer): optimize toAnyValue performance [#6287](https://github.com/open-telemetry/opentelemetry-js/pull/6287) @AbhiPrasad + ## 0.211.0 ### :boom: Breaking Changes diff --git a/experimental/packages/otlp-transformer/package.json b/experimental/packages/otlp-transformer/package.json index 97d484cddf3..c1cd9e63ddf 100644 --- a/experimental/packages/otlp-transformer/package.json +++ b/experimental/packages/otlp-transformer/package.json @@ -26,7 +26,7 @@ "submodule": "git submodule sync --recursive && git submodule update --init --recursive", "test": "nyc mocha 'test/**/*.test.ts'", "test:browser": "karma start --single-run", - "test:bench": "node test/performance/benchmark/index.js | tee .benchmark-results.txt", + "test:bench": "node test/performance/benchmark/micro-benchmarks.js && node test/performance/benchmark/index.js | tee .benchmark-results.txt", "prewatch": "node ../../../scripts/version-update.js", "watch": "npm run protos && tsc -w tsconfig.json tsconfig.esm.json tsconfig.esnext.json", "peer-api-check": "node ../../../scripts/peer-api-check.js", diff --git a/experimental/packages/otlp-transformer/src/common/internal.ts b/experimental/packages/otlp-transformer/src/common/internal.ts index cc9351a61fa..ba6d394cff0 100644 --- a/experimental/packages/otlp-transformer/src/common/internal.ts +++ b/experimental/packages/otlp-transformer/src/common/internal.ts @@ -64,16 +64,24 @@ export function toAnyValue(value: unknown): IAnyValue { } if (t === 'boolean') return { boolValue: value as boolean }; if (value instanceof Uint8Array) return { bytesValue: value }; - if (Array.isArray(value)) - return { arrayValue: { values: value.map(toAnyValue) } }; - if (t === 'object' && value != null) - return { - kvlistValue: { - values: Object.entries(value as object).map(([k, v]) => - toKeyValue(k, v) - ), - }, - }; + if (Array.isArray(value)) { + const values: IAnyValue[] = new Array(value.length); + for (let i = 0; i < value.length; i++) { + values[i] = toAnyValue(value[i]); + } + return { arrayValue: { values } }; + } + if (t === 'object' && value != null) { + const keys = Object.keys(value); + const values: IKeyValue[] = new Array(keys.length); + for (let i = 0; i < keys.length; i++) { + values[i] = { + key: keys[i], + value: toAnyValue((value as Record)[keys[i]]), + }; + } + return { kvlistValue: { values } }; + } return {}; } diff --git a/experimental/packages/otlp-transformer/test/performance/benchmark/index.js b/experimental/packages/otlp-transformer/test/performance/benchmark/index.js index db108af8c01..7115bcca1b9 100644 --- a/experimental/packages/otlp-transformer/test/performance/benchmark/index.js +++ b/experimental/packages/otlp-transformer/test/performance/benchmark/index.js @@ -14,55 +14,4 @@ * limitations under the License. */ -const Benchmark = require('benchmark'); -const { - createExportTraceServiceRequest, -} = require('../../../build/src/trace/internal'); -const { BasicTracerProvider } = require('@opentelemetry/sdk-trace-base'); -const { ProtobufTraceSerializer } = require('../../../build/src'); - -const tracerProvider = new BasicTracerProvider(); -const tracer = tracerProvider.getTracer('test'); - -const suite = new Benchmark.Suite(); - -const span = createSpan(); -const spans = []; -for (let i = 0; i < 100; i++) { - spans.push(createSpan()); -} - -suite.on('cycle', event => { - console.log(String(event.target)); -}); - -suite.add('transform 1 span', function () { - createExportTraceServiceRequest([span]); -}); - -suite.add('transform 100 spans', function () { - createExportTraceServiceRequest(spans); -}); - -suite.add('transform 100 spans to protobuf', function () { - ProtobufTraceSerializer.serializeRequest(spans); -}); - -suite.run(); - -function createSpan() { - const span = tracer.startSpan('span'); - span.setAttribute('aaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaa'); - span.setAttribute('bbbbbbbbbbbbbbbbbbbb', 'bbbbbbbbbbbbbbbbbbbb'); - span.setAttribute('cccccccccccccccccccc', 'cccccccccccccccccccc'); - span.setAttribute('dddddddddddddddddddd', 'dddddddddddddddddddd'); - span.setAttribute('eeeeeeeeeeeeeeeeeeee', 'eeeeeeeeeeeeeeeeeeee'); - span.setAttribute('ffffffffffffffffffff', 'ffffffffffffffffffff'); - span.setAttribute('gggggggggggggggggggg', 'gggggggggggggggggggg'); - span.setAttribute('hhhhhhhhhhhhhhhhhhhh', 'hhhhhhhhhhhhhhhhhhhh'); - span.setAttribute('iiiiiiiiiiiiiiiiiiii', 'iiiiiiiiiiiiiiiiiiii'); - span.setAttribute('jjjjjjjjjjjjjjjjjjjj', 'jjjjjjjjjjjjjjjjjjjj'); - span.end(); - - return span; -} +require('./transform'); diff --git a/experimental/packages/otlp-transformer/test/performance/benchmark/micro-benchmarks.js b/experimental/packages/otlp-transformer/test/performance/benchmark/micro-benchmarks.js new file mode 100644 index 00000000000..032bbea8d61 --- /dev/null +++ b/experimental/packages/otlp-transformer/test/performance/benchmark/micro-benchmarks.js @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require('./toAnyValue'); diff --git a/experimental/packages/otlp-transformer/test/performance/benchmark/toAnyValue.js b/experimental/packages/otlp-transformer/test/performance/benchmark/toAnyValue.js new file mode 100644 index 00000000000..4d16b1dc6a1 --- /dev/null +++ b/experimental/packages/otlp-transformer/test/performance/benchmark/toAnyValue.js @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Benchmark = require('benchmark'); +const { toAnyValue } = require('../../../build/src/common/internal'); + +const suite = new Benchmark.Suite(); + +suite.on('cycle', event => { + console.log(String(event.target)); +}); + +suite.add('toAnyValue string', function () { + toAnyValue('test-string-value'); +}); + +suite.add('toAnyValue integer', function () { + toAnyValue(42); +}); + +suite.add('toAnyValue array of strings', function () { + toAnyValue(['a', 'b', 'c', 'd', 'e']); +}); + +suite.add('toAnyValue array of objects', function () { + toAnyValue([{ a: 1 }, { b: 2 }, { c: 3 }]); +}); + +suite.add('toAnyValue nested object', function () { + toAnyValue({ level1: { level2: { level3: 'deep' } } }); +}); + +suite.add('toAnyValue complex nested', function () { + toAnyValue({ + a: { b: { c: { d: 'value' } } }, + arr: [1, 2, 3], + mixed: { nested: [{ x: 1 }, { y: 2 }] }, + }); +}); + +suite.run(); diff --git a/experimental/packages/otlp-transformer/test/performance/benchmark/transform.js b/experimental/packages/otlp-transformer/test/performance/benchmark/transform.js new file mode 100644 index 00000000000..db108af8c01 --- /dev/null +++ b/experimental/packages/otlp-transformer/test/performance/benchmark/transform.js @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Benchmark = require('benchmark'); +const { + createExportTraceServiceRequest, +} = require('../../../build/src/trace/internal'); +const { BasicTracerProvider } = require('@opentelemetry/sdk-trace-base'); +const { ProtobufTraceSerializer } = require('../../../build/src'); + +const tracerProvider = new BasicTracerProvider(); +const tracer = tracerProvider.getTracer('test'); + +const suite = new Benchmark.Suite(); + +const span = createSpan(); +const spans = []; +for (let i = 0; i < 100; i++) { + spans.push(createSpan()); +} + +suite.on('cycle', event => { + console.log(String(event.target)); +}); + +suite.add('transform 1 span', function () { + createExportTraceServiceRequest([span]); +}); + +suite.add('transform 100 spans', function () { + createExportTraceServiceRequest(spans); +}); + +suite.add('transform 100 spans to protobuf', function () { + ProtobufTraceSerializer.serializeRequest(spans); +}); + +suite.run(); + +function createSpan() { + const span = tracer.startSpan('span'); + span.setAttribute('aaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('bbbbbbbbbbbbbbbbbbbb', 'bbbbbbbbbbbbbbbbbbbb'); + span.setAttribute('cccccccccccccccccccc', 'cccccccccccccccccccc'); + span.setAttribute('dddddddddddddddddddd', 'dddddddddddddddddddd'); + span.setAttribute('eeeeeeeeeeeeeeeeeeee', 'eeeeeeeeeeeeeeeeeeee'); + span.setAttribute('ffffffffffffffffffff', 'ffffffffffffffffffff'); + span.setAttribute('gggggggggggggggggggg', 'gggggggggggggggggggg'); + span.setAttribute('hhhhhhhhhhhhhhhhhhhh', 'hhhhhhhhhhhhhhhhhhhh'); + span.setAttribute('iiiiiiiiiiiiiiiiiiii', 'iiiiiiiiiiiiiiiiiiii'); + span.setAttribute('jjjjjjjjjjjjjjjjjjjj', 'jjjjjjjjjjjjjjjjjjjj'); + span.end(); + + return span; +}