Skip to content

Commit e6909a3

Browse files
committed
fix(core): optimize warm cache performance for task execution
Batch daemon calls, add bulk cache resolution fast path, and parallelize output hash checking to dramatically improve warm cache hit performance. - Batch daemon calls for recordOutputsHash and outputsHashesMatch - Add resolveCachedTasksBulk fast path for bulk cache resolution - Cache readProjectsConfigurationFromProjectGraph in getExecutorForTask - Add verified match cache in daemon to skip redundant filesystem scans - Add Rayon-parallel get_files_for_outputs_batch in Rust - Batch-hash unhashed tasks per topological level - Skip recordOutputsHash for local-cache-kept-existing
1 parent 828303b commit e6909a3

File tree

19 files changed

+647
-81
lines changed

19 files changed

+647
-81
lines changed

astro-docs/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
"strict": true
2121
},
2222
"references": [
23+
{
24+
"path": "../packages/nx"
25+
},
2326
{
2427
"path": "../nx-dev/ui-markdoc"
2528
},

benchmarks/goals.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@
1010
},
1111
"copy-warm": {
1212
"max": 1.0
13+
},
14+
"build-warm": {
15+
"max": 5.0
1316
}
1417
}

benchmarks/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"bench:show-projects": "NX_NO_CLOUD=true hyperfine --show-output --setup 'nx reset' --warmup 1 --min-runs 5 --export-json results-show-projects.json 'nx show projects --tui=false'",
1212
"bench:cat-warm": "NX_NO_CLOUD=true hyperfine --show-output --setup 'nx reset' --warmup 1 --min-runs 5 --export-json results-cat-warm.json 'nx run-many -t cat --tui=false'",
1313
"bench:copy-warm": "NX_NO_CLOUD=true hyperfine --show-output --setup 'nx reset' --warmup 1 --min-runs 5 --export-json results-copy-warm.json 'nx run-many -t copy --tui=false'",
14-
"disabled:bench:build-warm": "NX_NO_CLOUD=true hyperfine --show-output --setup 'nx reset' --warmup 1 --min-runs 5 --export-json results-build-warm.json 'nx run-many -t build --tui=true --tui-auto-exit'"
14+
"bench:build-warm": "NX_NO_CLOUD=true hyperfine --ignore-failure --setup 'nx reset' --warmup 1 --min-runs 5 --export-json results-build-warm.json 'nx run-many -t build --tui=true --tui-auto-exit'"
1515
},
1616
"nx": {
1717
"targets": {
@@ -20,7 +20,8 @@
2020
"bench:version",
2121
"bench:show-projects",
2222
"bench:cat-warm",
23-
"bench:copy-warm"
23+
"bench:copy-warm",
24+
"bench:build-warm"
2425
]
2526
},
2627
"bench:version": {
@@ -47,7 +48,7 @@
4748
],
4849
"parallelism": false
4950
},
50-
"disabled:bench:build-warm": {
51+
"bench:build-warm": {
5152
"dependsOn": [
5253
"^build"
5354
],

e2e/nx-init/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
"include": [],
77
"files": [],
88
"references": [
9+
{
10+
"path": "../../packages/nx"
11+
},
912
{
1013
"path": "../utils"
1114
},

nx-dev/ui-markdoc/tsconfig.lib.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
],
2929
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"],
3030
"references": [
31+
{
32+
"path": "../../packages/nx/tsconfig.lib.json"
33+
},
3134
{
3235
"path": "../feature-analytics/tsconfig.lib.json"
3336
},

packages/create-nx-workspace/tsconfig.lib.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@
1414
"jest.config.ts"
1515
],
1616
"include": ["**/*.ts", "*.json", "migrations.json"],
17-
"references": []
17+
"references": [
18+
{
19+
"path": "../nx/tsconfig.lib.json"
20+
}
21+
]
1822
}

packages/nest/tsconfig.lib.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
],
1818
"include": ["**/*.ts"],
1919
"references": [
20+
{
21+
"path": "../nx/tsconfig.lib.json"
22+
},
2023
{
2124
"path": "../node/tsconfig.lib.json"
2225
},

packages/nx/src/daemon/client/client.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,15 @@ export class DaemonClient {
795795
});
796796
}
797797

798+
recordOutputsHashBatch(
799+
entries: { outputs: string[]; hash: string }[]
800+
): Promise<any> {
801+
return this.sendToDaemonViaQueue({
802+
type: 'RECORD_OUTPUTS_HASH_BATCH',
803+
data: entries,
804+
});
805+
}
806+
798807
outputsHashesMatch(outputs: string[], hash: string): Promise<any> {
799808
return this.sendToDaemonViaQueue({
800809
type: 'OUTPUTS_HASHES_MATCH',
@@ -805,6 +814,15 @@ export class DaemonClient {
805814
});
806815
}
807816

817+
outputsHashesMatchBatch(
818+
entries: { outputs: string[]; hash: string }[]
819+
): Promise<boolean[]> {
820+
return this.sendToDaemonViaQueue({
821+
type: 'OUTPUTS_HASHES_MATCH_BATCH',
822+
data: entries,
823+
});
824+
}
825+
808826
glob(globs: string[], exclude?: string[]): Promise<string[]> {
809827
const message: HandleGlobMessage = {
810828
type: 'GLOB',

packages/nx/src/daemon/server/handle-outputs-tracking.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { HandlerResult } from './server';
2-
import { outputsHashesMatch, recordOutputsHash } from './outputs-tracking';
2+
import {
3+
outputsHashesMatch,
4+
outputsHashesMatchBatch,
5+
recordOutputsHash,
6+
recordOutputsHashBatch,
7+
} from './outputs-tracking';
38

49
export async function handleRecordOutputsHash(payload: {
510
type: string;
@@ -21,6 +26,26 @@ export async function handleRecordOutputsHash(payload: {
2126
}
2227
}
2328

29+
export async function handleRecordOutputsHashBatch(payload: {
30+
type: string;
31+
data: { outputs: string[]; hash: string }[];
32+
}): Promise<HandlerResult> {
33+
try {
34+
recordOutputsHashBatch(payload.data);
35+
return {
36+
description: 'recordOutputsHashBatch',
37+
response: '{}',
38+
};
39+
} catch (e) {
40+
return {
41+
description: 'recordOutputsHashBatch failed',
42+
error: new Error(
43+
`Critical error when recording metadata about outputs: '${e.message}'.`
44+
),
45+
};
46+
}
47+
}
48+
2449
export async function handleOutputsHashesMatch(payload: {
2550
type: string;
2651
data: { outputs: string[]; hash: string };
@@ -43,3 +68,23 @@ export async function handleOutputsHashesMatch(payload: {
4368
};
4469
}
4570
}
71+
72+
export async function handleOutputsHashesMatchBatch(payload: {
73+
type: string;
74+
data: { outputs: string[]; hash: string }[];
75+
}): Promise<HandlerResult> {
76+
try {
77+
const results = outputsHashesMatchBatch(payload.data);
78+
return {
79+
response: results,
80+
description: 'outputsHashesMatchBatch',
81+
};
82+
} catch (e) {
83+
return {
84+
description: 'outputsHashesMatchBatch failed',
85+
error: new Error(
86+
`Critical error when verifying the contents of the outputs haven't changed: '${e.message}'.`
87+
),
88+
};
89+
}
90+
}

packages/nx/src/daemon/server/outputs-tracking.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { dirname } from 'path';
2-
import { WatchEvent, getFilesForOutputs } from '../../native';
2+
import {
3+
WatchEvent,
4+
getFilesForOutputs,
5+
getFilesForOutputsBatch,
6+
} from '../../native';
37
import { collapseExpandedOutputs } from '../../utils/collapse-expanded-outputs';
48
import { workspaceRoot } from '../../utils/workspace-root';
59

@@ -77,6 +81,7 @@ export function processFileChangesInOutputs(
7781
if (dirsContainingOutputs[current]) {
7882
dirsContainingOutputs[current].forEach((output) => {
7983
if (now - timestamps[output] > 2000) {
84+
const hash = recordedHashes[output];
8085
recordedHashes[output] = undefined;
8186
}
8287
});
@@ -94,6 +99,46 @@ export function processFileChangesInOutputs(
9499
}
95100
}
96101

102+
/**
103+
* Batch version of outputsHashesMatch that uses Rayon-parallel
104+
* filesystem scanning for uncached entries.
105+
*/
106+
export function outputsHashesMatchBatch(
107+
entries: { outputs: string[]; hash: string }[]
108+
): boolean[] {
109+
if (disabled) return entries.map(() => false);
110+
111+
// Batch filesystem scan in parallel via Rust/Rayon
112+
const outputsBatch = entries.map((e) => e.outputs);
113+
const expandedBatch = getFilesForOutputsBatch(workspaceRoot, outputsBatch);
114+
115+
const results: boolean[] = [];
116+
for (let i = 0; i < entries.length; i++) {
117+
const expanded = collapseExpandedOutputs(expandedBatch[i]);
118+
results.push(_outputsHashesMatch(expanded, entries[i].hash));
119+
}
120+
121+
return results;
122+
}
123+
124+
/**
125+
* Batch version of recordOutputsHash that uses Rayon-parallel
126+
* filesystem scanning.
127+
*/
128+
export function recordOutputsHashBatch(
129+
entries: { outputs: string[]; hash: string }[]
130+
) {
131+
if (disabled) return;
132+
133+
const outputsBatch = entries.map((e) => e.outputs);
134+
const expandedBatch = getFilesForOutputsBatch(workspaceRoot, outputsBatch);
135+
136+
for (let i = 0; i < entries.length; i++) {
137+
const expanded = collapseExpandedOutputs(expandedBatch[i]);
138+
_recordOutputsHash(expanded, entries[i].hash);
139+
}
140+
}
141+
97142
export function disableOutputsTracking() {
98143
disabled = true;
99144
}

0 commit comments

Comments
 (0)