diff --git a/benchmark/name-caching.js b/benchmark/name-caching.js new file mode 100644 index 00000000..bb17eb9f --- /dev/null +++ b/benchmark/name-caching.js @@ -0,0 +1,108 @@ +'use strict' + +const { Script } = require('vm') +const { isMainThread } = require('worker_threads') +const { TimeProfiler } = require('../out/src/time-profiler-bindings') + +const scriptCount = Number(process.env.SCRIPT_COUNT || '500') +const functionsPerScript = Number(process.env.FUNCTIONS_PER_SCRIPT || '8') +const rounds = Number(process.env.ROUNDS || '8') +const work = Number(process.env.WORK || '1000') +const lines = Number(process.env.LINES || '32') +const iterations = Number(process.env.ITERATIONS || '9') +const warmup = Number(process.env.WARMUP || '3') +const nonAsciiNames = process.env.NON_ASCII_NAMES === '1' + +function compileScript (scriptIndex) { + const handlers = [] + const calls = [] + + for (let i = 0; i < functionsPerScript; i++) { + const name = `${nonAsciiNames ? '处理器' : 'handler'}_${scriptIndex}_${i}` + const statements = Array.from({ length: lines }, (_, line) => + `for (let k = 0; k < ${work}; k++) total += Math.sqrt(k * n + ${i + line})` + ) + handlers.push(` + function ${name}(n) { + let total = 0 + ${statements.join('\n')} + return total + }`) + calls.push(`total += ${name}(n + ${i})`) + } + + return new Script(` + (() => { + ${handlers.join('\n')} + return function ${nonAsciiNames ? '运行' : 'run'}_${scriptIndex}(n) { + let total = 0 + ${calls.join('\n')} + return total + } + })() + `, { filename: `/opt/${nonAsciiNames ? '服务' : 'service'}/dist/modules/module-${scriptIndex}.js` }) + .runInThisContext() +} + +function countNodes (node) { + let count = 1 + for (const child of node.children) count += countNodes(child) + return count +} + +function median (values) { + return [...values].sort((a, b) => a - b)[Math.floor(values.length / 2)] +} + +const scripts = Array.from({ length: scriptCount }, (_, i) => compileScript(i)) +const stopMicros = [] +const nanosPerNode = [] +const nodeCounts = [] + +for (let iteration = 0; iteration < warmup + iterations; iteration++) { + const profiler = new TimeProfiler({ + intervalMicros: 50, + durationMillis: 60000, + lineNumbers: true, + withContexts: false, + workaroundV8Bug: false, + collectCpuTime: false, + collectAsyncId: false, + isMainThread, + useCPED: false + }) + profiler.start() + + let result = 0 + for (let round = 0; round < rounds; round++) { + for (const run of scripts) result += run(round + 1) + } + + const start = process.hrtime.bigint() + const profile = profiler.stop(false) + const elapsed = Number(process.hrtime.bigint() - start) + profiler.dispose() + + if (!Number.isFinite(result)) throw new Error('benchmark workload failed') + if (iteration < warmup) continue + + const nodes = countNodes(profile.topDownRoot) + stopMicros.push(elapsed / 1000) + nanosPerNode.push(elapsed / nodes) + nodeCounts.push(nodes) +} + +console.log(JSON.stringify({ + name: 'profile-name-caching', + scripts: scriptCount, + functionsPerScript, + lines, + nonAsciiNames, + rounds, + work, + iterations, + warmup, + medianNodes: median(nodeCounts), + medianStopMicros: median(stopMicros), + medianNanosPerNode: median(nanosPerNode) +})) diff --git a/bindings/translate-time-profile.cc b/bindings/translate-time-profile.cc index 4a915a6f..fb997da2 100644 --- a/bindings/translate-time-profile.cc +++ b/bindings/translate-time-profile.cc @@ -344,6 +344,8 @@ class TimeProfileTranslator : ProfileTranslator { unsigned int hitLineCount = node->GetHitLineCount(); unsigned int hitCount = node->GetHitCount(); + auto name = node->GetFunctionName(); + auto scriptName = node->GetScriptResourceName(); auto scriptId = NewInteger(node->GetScriptId()); if (hitLineCount > 0) { std::vector entries(hitLineCount); @@ -352,8 +354,8 @@ class TimeProfileTranslator : ProfileTranslator { for (const v8::CpuProfileNode::LineTick entry : entries) { Set(children, index++, - CreateTimeNode(node->GetFunctionName(), - node->GetScriptResourceName(), + CreateTimeNode(name, + scriptName, scriptId, NewInteger(entry.line), // V8 14+ (Node.js 25+) added column field to LineTick struct @@ -372,8 +374,8 @@ class TimeProfileTranslator : ProfileTranslator { children = NewArray(count + 1); Set(children, index++, - CreateTimeNode(node->GetFunctionName(), - node->GetScriptResourceName(), + CreateTimeNode(name, + scriptName, scriptId, NewInteger(node->GetLineNumber()), NewInteger(node->GetColumnNumber()), @@ -387,17 +389,21 @@ class TimeProfileTranslator : ProfileTranslator { for (int32_t i = 0; i < count; i++) { Set(children, index++, - TranslateLineNumbersTimeProfileNode(node, node->GetChild(i))); + TranslateLineNumbersTimeProfileNode( + name, scriptName, scriptId, node->GetChild(i))); }; return children; } v8::Local TranslateLineNumbersTimeProfileNode( - const v8::CpuProfileNode* parent, const v8::CpuProfileNode* node) { - return CreateTimeNode(parent->GetFunctionName(), - parent->GetScriptResourceName(), - NewInteger(parent->GetScriptId()), + v8::Local name, + v8::Local scriptName, + v8::Local scriptId, + const v8::CpuProfileNode* node) { + return CreateTimeNode(name, + scriptName, + scriptId, NewInteger(node->GetLineNumber()), NewInteger(node->GetColumnNumber()), zero,