Perf: worker farm

This commit is contained in:
SukkaW
2026-09-02 20:01:48 +08:00
parent 6449aabce7
commit 9b155695e7
11 changed files with 87 additions and 92 deletions

View File

@@ -11,7 +11,7 @@ import { appendArrayInPlace } from 'foxts/append-array-in-place';
import { isMainThread } from 'node:worker_threads';
import { resolveStrategyOutputPath, serializeStrategy, writeDataToStrategies } from './strategy-write-data';
import type { OutputWorkerPayload, StrategyWriteData } from './strategy-write-data';
import { getOutputWorkerFarm } from './output-worker-farm';
import { getBuildWorkerFarm } from '../build-worker-farm';
/**
* Below this many dumped domain entries, formatting + hashing + writing inline is
@@ -506,8 +506,8 @@ export class FileOutput {
// synchronously so no completion ever waits on a busy main thread.
//
// Only worth doing from the main thread -- tasks that already run entirely on
// a worker (build-microsoft-cdn, build-telegram-cidr, build-cdn-download-conf)
// are not contending with anything, and must not spawn a nested worker farm.
// a worker (build-microsoft-cdn, build-cdn-download-conf) are not contending
// with anything, and must not spawn a nested worker farm.
if (isMainThread && domains.length >= OUTPUT_WORKER_THRESHOLD) {
this.guardBeforeWritingToStrategies();
@@ -522,7 +522,7 @@ export class FileOutput {
return childSpan.traceWorkerChild(
'write via output worker',
rawSpan => getOutputWorkerFarm().writeOutput(rawSpan, payload)
rawSpan => getBuildWorkerFarm().writeOutput(rawSpan, payload)
);
}

View File

@@ -1,40 +0,0 @@
import type { JestWorkerFarm } from 'jest-worker';
import { createWorker } from '../worker';
type OutputWorkerModule = typeof import('./output.worker');
type OutputWorkerFarm = JestWorkerFarm<Pick<OutputWorkerModule, 'writeOutput'>>;
let farm: OutputWorkerFarm | null = null;
/**
* Lazily boot the output worker farm. Only FileOutput#write dispatches here, and
* only when an output crosses the offload threshold -- today that is exclusively
* the reject domainsets / adguardhome outputs of build-reject-domainset.
*
* IMPORTANT: whoever triggers the lazy boot is responsible for a matching
* endOutputWorkerFarm() (idempotent, safe to call unconditionally), otherwise the
* worker threads keep a standalone task run alive.
*/
export function getOutputWorkerFarm(): OutputWorkerFarm {
// 3 workers: reject, reject_extra and reject_phishing format & write in parallel
farm ??= createWorker<OutputWorkerModule>(require.resolve('./output.worker'), 3)(['writeOutput']);
return farm;
}
/**
* Boot the farm ahead of time. Spawning the threads costs ~200-300ms (each loads
* @swc-node/register and compiles the module graph), and since every big output
* dispatches at the very end of a task, that cost otherwise lands entirely on the
* critical path. Call this as early as the task starts so it overlaps the downloads.
*/
export function warmOutputWorkerFarm(): void {
getOutputWorkerFarm();
}
export async function endOutputWorkerFarm(): Promise<void> {
if (farm) {
const f = farm;
farm = null;
await f.end();
}
}