From c1862c67fc389b44ee18a4acb912bbcbf8aa3c70 Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 6 Aug 2026 13:49:07 +0800 Subject: [PATCH] perf(fmt): prioritize Markdown worker tasks --- packages/rstack/src/fmt/runner.ts | 44 +++++++++++++++++-- packages/rstack/src/fmt/workerPool.ts | 2 + .../tests/fmt/runnerWriteFailure.test.ts | 1 + 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/rstack/src/fmt/runner.ts b/packages/rstack/src/fmt/runner.ts index aa0cc5c7..0f9c92cf 100644 --- a/packages/rstack/src/fmt/runner.ts +++ b/packages/rstack/src/fmt/runner.ts @@ -16,6 +16,17 @@ interface FmtWorkerPoolResult { processedFileCount: number; } +/** Benchmarks show stable scheduling gains only when at least eight workers share the queue. */ +const minPriorityWorkers = 8; + +/** + * Markdown parsing is consistently slower in representative repositories. Keep this signal + * narrow: parser overrides and file size can outweigh the extension, and deferring every JS/TS + * file could turn a large source file into the final straggler. + */ +const isMarkdown = (file: FmtFileRequest): boolean => + file.path.endsWith('.md') || file.path.endsWith('.mdx'); + /** Converts a formatter outcome into the shared per-file result. */ const runFmtFile = async ( file: FmtFileRequest, @@ -41,6 +52,30 @@ const runFmtFile = async ( } }; +/** Starts slower Markdown parsers first while preserving order within both priority groups. */ +const runPriorityFmtFiles = async ( + files: FmtFileRequest[], + shouldWrite: boolean, + formatFile: FormatFile, +): Promise => { + const priority: number[] = []; + const rest: number[] = []; + + for (let index = 0; index < files.length; index++) { + (isMarkdown(files[index]) ? priority : rest).push(index); + } + + const order = priority.concat(rest); + const outcomes = await Promise.all( + order.map((index) => runFmtFile(files[index], shouldWrite, formatFile)), + ); + const results = new Array(files.length); + for (let index = 0; index < order.length; index++) { + results[order[index]] = outcomes[index]; + } + return results; +}; + /** Processes files in a worker pool while preserving input order. */ const runFmtFilesInWorkerPool = async ( files: FmtFileRequest[], @@ -51,9 +86,12 @@ const runFmtFilesInWorkerPool = async ( const workerPool = await createFmtWorkerPool(files.length, maxWorkers); try { - const results = await Promise.all( - files.map((file) => runFmtFile(file, shouldWrite, workerPool.formatFile)), - ); + const results = + workerPool.workerCount >= minPriorityWorkers + ? await runPriorityFmtFiles(files, shouldWrite, workerPool.formatFile) + : await Promise.all( + files.map((file) => runFmtFile(file, shouldWrite, workerPool.formatFile)), + ); const processedFiles: FmtFileResult[] = []; let processedFileCount = 0; diff --git a/packages/rstack/src/fmt/workerPool.ts b/packages/rstack/src/fmt/workerPool.ts index 974dd38d..1531a864 100644 --- a/packages/rstack/src/fmt/workerPool.ts +++ b/packages/rstack/src/fmt/workerPool.ts @@ -7,6 +7,7 @@ import type { FmtFileRequest } from './types.ts'; type FmtWorkerMethods = typeof import('./worker.ts'); interface FmtWorkerPool { + readonly workerCount: number; formatFile: ( file: FmtFileRequest, shouldWrite: boolean, @@ -55,6 +56,7 @@ const createFmtWorkerPool = async ( } return { + workerCount, formatFile: (file, shouldWrite) => pool.run({ file, shouldWrite }, { name: 'formatFile' }), terminate: () => pool.destroy(), }; diff --git a/packages/rstack/tests/fmt/runnerWriteFailure.test.ts b/packages/rstack/tests/fmt/runnerWriteFailure.test.ts index a7ea7b45..efb96dfd 100644 --- a/packages/rstack/tests/fmt/runnerWriteFailure.test.ts +++ b/packages/rstack/tests/fmt/runnerWriteFailure.test.ts @@ -8,6 +8,7 @@ const mocks = rs.hoisted(() => ({ rs.mock('../../src/fmt/workerPool.ts', () => ({ createFmtWorkerPool: () => Promise.resolve({ + workerCount: 1, formatFile: () => Promise.reject(new Error('file write failed')), terminate: () => { mocks.terminateCalls++;