diff --git a/.github/workflows/acme-ci.yml b/.github/workflows/acme-ci.yml index 70dc378..22ed623 100644 --- a/.github/workflows/acme-ci.yml +++ b/.github/workflows/acme-ci.yml @@ -48,8 +48,9 @@ jobs: - run: npm ci - run: npm run profile:cpu env: - PROFILE_DURATION_MS: 45000 - PROFILE_CPU_WORKERS: 8 + PROFILE_CPU_TASKS: 256 + PROFILE_CPU_ITERATIONS: 6250000 + PROFILE_CPU_WORKERS: 32 unit-tests: name: Unit tests shard ${{ matrix.shard }}/8 diff --git a/solutions-engineering/migration-lab/app/scripts/resource-profile.js b/solutions-engineering/migration-lab/app/scripts/resource-profile.js index d66a5f1..587ad4b 100644 --- a/solutions-engineering/migration-lab/app/scripts/resource-profile.js +++ b/solutions-engineering/migration-lab/app/scripts/resource-profile.js @@ -1,4 +1,5 @@ const os = require('os'); +const crypto = require('crypto'); const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); function durationMs() { @@ -9,19 +10,28 @@ function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } -function burnCpu(ms) { - const end = Date.now() + ms; - let value = 0; +function iterationCount() { + return Number(process.env.PROFILE_CPU_ITERATIONS || 45000000); +} - while (Date.now() < end) { - value += Math.sqrt((value % 1000) + Math.random()); - } +function taskCount() { + return Number(process.env.PROFILE_CPU_TASKS || 32); +} + +function runFraudScoringBatch(iterations) { + const output = crypto.pbkdf2Sync( + 'acme-payments-risk-input', + 'merchant-risk-model-v3', + iterations, + 64, + 'sha512', + ); - return value; + return output.readUInt32BE(0); } if (!isMainThread) { - const value = burnCpu(workerData.durationMs); + const value = runFraudScoringBatch(workerData.iterations); parentPort.postMessage(value); return; } @@ -34,24 +44,37 @@ async function runIdleProfile() { console.log('background report profile completed'); } +function runCpuTask(iterations) { + return new Promise((resolve, reject) => { + const worker = new Worker(__filename, { workerData: { iterations } }); + worker.once('message', resolve); + worker.once('error', reject); + worker.once('exit', (code) => { + if (code !== 0) { + reject(new Error(`worker exited with code ${code}`)); + } + }); + }); +} + async function runCpuProfile() { - const duration = durationMs(); - const workers = Number(process.env.PROFILE_CPU_WORKERS || Math.max(4, os.cpus().length * 2)); + const workers = Number(process.env.PROFILE_CPU_WORKERS || Math.max(2, os.cpus().length)); + const tasks = taskCount(); + const iterations = iterationCount(); + let nextTask = 0; - console.log(`starting fraud model profile with workers=${workers} duration_ms=${duration}`); + console.log(`starting fraud model profile with workers=${workers} tasks=${tasks} iterations_per_task=${iterations}`); console.log(`detected_cpu_count=${os.cpus().length}`); + async function runWorkerQueue() { + while (nextTask < tasks) { + nextTask += 1; + await runCpuTask(iterations); + } + } + await Promise.all( - Array.from({ length: workers }, () => new Promise((resolve, reject) => { - const worker = new Worker(__filename, { workerData: { durationMs: duration } }); - worker.once('message', resolve); - worker.once('error', reject); - worker.once('exit', (code) => { - if (code !== 0) { - reject(new Error(`worker exited with code ${code}`)); - } - }); - })), + Array.from({ length: Math.min(workers, tasks) }, () => runWorkerQueue()), ); console.log('fraud model profile completed');