Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions benchmark/name-caching.js
Original file line number Diff line number Diff line change
@@ -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)
}))
24 changes: 15 additions & 9 deletions bindings/translate-time-profile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<v8::CpuProfileNode::LineTick> entries(hitLineCount);
Expand All @@ -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
Expand All @@ -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()),
Expand All @@ -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<v8::Object> TranslateLineNumbersTimeProfileNode(
const v8::CpuProfileNode* parent, const v8::CpuProfileNode* node) {
return CreateTimeNode(parent->GetFunctionName(),
parent->GetScriptResourceName(),
NewInteger(parent->GetScriptId()),
v8::Local<v8::String> name,
v8::Local<v8::String> scriptName,
v8::Local<v8::Integer> scriptId,
const v8::CpuProfileNode* node) {
return CreateTimeNode(name,
scriptName,
scriptId,
NewInteger(node->GetLineNumber()),
NewInteger(node->GetColumnNumber()),
zero,
Expand Down
Loading