perf(package-manager): parallelize cold-batch slot linking#12249
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🧰 Additional context used📓 Path-based instructions (1)pacquet/**/*.rs📄 CodeRabbit inference engine (pacquet/AGENTS.md)
Files:
🧠 Learnings (25)📓 Common learnings📚 Learning: 2026-05-23T09:14:43.635ZApplied to files:
📚 Learning: 2026-06-04T14:40:29.444ZApplied to files:
📚 Learning: 2026-05-24T21:11:04.272ZApplied to files:
📚 Learning: 2026-05-23T09:14:43.635ZApplied to files:
📚 Learning: 2026-05-24T21:11:04.272ZApplied to files:
📚 Learning: 2026-05-29T18:03:15.372ZApplied to files:
📚 Learning: 2026-05-24T16:07:54.784ZApplied to files:
📚 Learning: 2026-05-20T23:23:43.636ZApplied to files:
📚 Learning: 2026-06-02T13:18:30.659ZApplied to files:
📚 Learning: 2026-05-19T19:23:00.981ZApplied to files:
📚 Learning: 2026-05-20T19:40:55.051ZApplied to files:
📚 Learning: 2026-05-22T00:08:44.646ZApplied to files:
📚 Learning: 2026-05-20T23:07:58.444ZApplied to files:
📚 Learning: 2026-06-06T18:58:37.156ZApplied to files:
📚 Learning: 2026-05-23T16:55:36.507ZApplied to files:
📚 Learning: 2026-05-20T21:18:56.391ZApplied to files:
📚 Learning: 2026-05-29T18:03:15.372ZApplied to files:
📚 Learning: 2026-05-29T18:03:15.372ZApplied to files:
📚 Learning: 2026-05-29T18:03:15.372ZApplied to files:
📚 Learning: 2026-05-25T14:58:11.105ZApplied to files:
📚 Learning: 2026-06-04T20:24:32.096ZApplied to files:
📚 Learning: 2026-05-20T01:42:44.958ZApplied to files:
📚 Learning: 2026-05-04T17:01:30.322ZApplied to files:
📚 Learning: 2026-06-04T06:04:05.107ZApplied to files:
🔇 Additional comments (7)
📝 WalkthroughWalkthrough
ChangesCold-batch slot linking deferral
Sequence Diagram(s)sequenceDiagram
participant CreateVirtualStore
participant ColdInstall as InstallPackageBySnapshot<br/>(defer_link=true)
participant ColdFetch as Cold fetch/cache
participant ColdLinkPass as Cold link pass<br/>(Rayon parallel)
participant CreateVirtualDir as CreateVirtualDirBySnapshot
CreateVirtualStore->>ColdInstall: install with defer_link=true
ColdInstall->>ColdFetch: fetch & extract, skip slot
ColdFetch-->>CreateVirtualStore: return cas_paths
CreateVirtualStore->>ColdLinkPass: parallel slot linking
ColdLinkPass->>CreateVirtualDir: materialize slots (isolated)
CreateVirtualDir-->>ColdLinkPass: slot populated
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Micro-Benchmark ResultsLinux |
Integrated-Benchmark Report (Linux)Each scenario has pacquet rows (direct install) and pnpr rows (the same client through the pnpr install accelerator), so pnpr@HEAD vs pacquet@HEAD is the pnpr-vs-direct ratio. Cold-store scenarios wipe the client store between runs (warm server); hot-store scenarios keep it warm. The pacquet@HEAD rows feed the pacquet Bencher testbed; the pnpr@HEAD rows feed the pnpr testbed. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 10.212459317440002,
"stddev": 0.16210709804143753,
"median": 10.13593329954,
"user": 3.2735745799999996,
"system": 3.2884268399999996,
"min": 10.08858325904,
"max": 10.56932165204,
"times": [
10.37342097604,
10.34087125404,
10.15279356304,
10.11900044504,
10.08858325904,
10.09103396604,
10.17843960504,
10.09205541804,
10.11907303604,
10.56932165204
]
},
{
"command": "pacquet@main",
"mean": 9.90931747134,
"stddev": 0.1425936664770252,
"median": 9.86791610454,
"user": 3.54759868,
"system": 4.300904539999999,
"min": 9.84366085404,
"max": 10.31248548004,
"times": [
9.88692714604,
9.84366085404,
9.84369774904,
9.87077879704,
9.85985611504,
9.86505341204,
9.84910738104,
10.31248548004,
9.89059695904,
9.87101082004
]
},
{
"command": "pnpr@HEAD",
"mean": 5.366586183940001,
"stddev": 0.15476657800863963,
"median": 5.31164598704,
"user": 2.4440086799999996,
"system": 2.96750124,
"min": 5.26085097104,
"max": 5.78930338904,
"times": [
5.78930338904,
5.29576767704,
5.42573014504,
5.29418664704,
5.26085097104,
5.30205793204,
5.31528652304,
5.33497270104,
5.33970040304,
5.30800545104
]
},
{
"command": "pnpr@main",
"mean": 5.126716651440001,
"stddev": 0.14086727968297946,
"median": 5.08508243504,
"user": 2.7235409799999992,
"system": 3.9439124399999996,
"min": 5.02323063504,
"max": 5.50713962404,
"times": [
5.12610444904,
5.04106670804,
5.13302090604,
5.50713962404,
5.02323063504,
5.05289407004,
5.06209018304,
5.15685198504,
5.05669326704,
5.10807468704
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6492787357600001,
"stddev": 0.01100749143187808,
"median": 0.6471822676600001,
"user": 0.37601122,
"system": 1.2943588799999999,
"min": 0.63189176266,
"max": 0.6665447226600001,
"times": [
0.6665447226600001,
0.6544696776600001,
0.64766798966,
0.66288860266,
0.64507346966,
0.63189176266,
0.6384632786600001,
0.6577256676600001,
0.64669654566,
0.64136564066
]
},
{
"command": "pacquet@main",
"mean": 0.70164067616,
"stddev": 0.0330050551836713,
"median": 0.69087077916,
"user": 0.39105581999999994,
"system": 1.3229494800000001,
"min": 0.67355131566,
"max": 0.78715875566,
"times": [
0.72544138766,
0.67355131566,
0.68766836966,
0.6847879946600001,
0.68989504266,
0.78715875566,
0.69413707566,
0.69184651566,
0.69925473266,
0.6826655716600001
]
},
{
"command": "pnpr@HEAD",
"mean": 0.7979088221599999,
"stddev": 0.06305522350005904,
"median": 0.76793710066,
"user": 0.39382391999999994,
"system": 1.3141035799999998,
"min": 0.7605529896600001,
"max": 0.96459612366,
"times": [
0.80702072666,
0.7653408686600001,
0.96459612366,
0.83267479566,
0.78430876766,
0.7674035276600001,
0.76789384766,
0.76131622066,
0.7605529896600001,
0.7679803536600001
]
},
{
"command": "pnpr@main",
"mean": 0.7816471971600001,
"stddev": 0.016306291826892586,
"median": 0.77500490716,
"user": 0.41015262,
"system": 1.3126817799999997,
"min": 0.76078944666,
"max": 0.81164446966,
"times": [
0.78205740566,
0.79204231666,
0.81164446966,
0.7733678636600001,
0.80514638266,
0.76078944666,
0.7766419506600001,
0.77102867566,
0.7713977416600001,
0.7723557186600001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 5.35499403886,
"stddev": 0.04771691585975282,
"median": 5.361685317659999,
"user": 3.71947504,
"system": 3.3281736399999993,
"min": 5.28880688966,
"max": 5.43715723466,
"times": [
5.36599188766,
5.33051845766,
5.40323957366,
5.38867771566,
5.357378747659999,
5.36786015566,
5.30072383966,
5.30958588666,
5.43715723466,
5.28880688966
]
},
{
"command": "pacquet@main",
"mean": 5.3684357382600005,
"stddev": 0.049909525486640134,
"median": 5.36137162216,
"user": 3.7881255400000002,
"system": 3.33353514,
"min": 5.32693433366,
"max": 5.50098405266,
"times": [
5.36585538166,
5.35931089666,
5.33290394866,
5.50098405266,
5.37734052566,
5.37593488666,
5.34754359266,
5.32693433366,
5.36343234766,
5.33411741666
]
},
{
"command": "pnpr@HEAD",
"mean": 1.7724795680600003,
"stddev": 0.028164753367486778,
"median": 1.7811691221600001,
"user": 2.4031990399999996,
"system": 2.7506211399999994,
"min": 1.7298924416600001,
"max": 1.8108298856600002,
"times": [
1.7622365476600002,
1.79691764666,
1.7943162006600002,
1.7500980326600002,
1.7772334476600002,
1.7304997316600002,
1.7876669496600002,
1.78510479666,
1.7298924416600001,
1.8108298856600002
]
},
{
"command": "pnpr@main",
"mean": 2.0293010547600003,
"stddev": 0.028902669216662114,
"median": 2.0282530466599997,
"user": 2.49159284,
"system": 3.2863611400000003,
"min": 1.98934768566,
"max": 2.09044191366,
"times": [
2.00674834366,
2.03331789166,
1.9992147856600002,
2.02318820166,
2.0518348996599998,
2.03991600266,
2.0225736096599998,
2.0364272136599997,
2.09044191366,
1.98934768566
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.41480123434,
"stddev": 0.022782186949906365,
"median": 1.42414865764,
"user": 1.5848426799999997,
"system": 1.7562596600000002,
"min": 1.3614215491400001,
"max": 1.43196247514,
"times": [
1.4299578831400002,
1.42227660014,
1.43196247514,
1.42370714414,
1.40856440414,
1.3614215491400001,
1.38922278914,
1.42459017114,
1.42525408314,
1.4310552441400002
]
},
{
"command": "pacquet@main",
"mean": 1.41777751304,
"stddev": 0.022547579981766656,
"median": 1.42197570014,
"user": 1.5929027799999997,
"system": 1.7660905599999999,
"min": 1.38559587314,
"max": 1.4628841861400002,
"times": [
1.4268732191400002,
1.42202540414,
1.4628841861400002,
1.3973995031400002,
1.4234702621400002,
1.4219259961400001,
1.43618972514,
1.3977309901400001,
1.38559587314,
1.40367997114
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6815159390399999,
"stddev": 0.07013368996278802,
"median": 0.66236200614,
"user": 0.34128188,
"system": 1.2554596600000003,
"min": 0.64459466914,
"max": 0.87976921814,
"times": [
0.66889696314,
0.65113203514,
0.64459466914,
0.87976921814,
0.66099548614,
0.65200971714,
0.65882153714,
0.66372852614,
0.66966949214,
0.66554174614
]
},
{
"command": "pnpr@main",
"mean": 0.69488855614,
"stddev": 0.056728181033914624,
"median": 0.66980846864,
"user": 0.35591158,
"system": 1.27355596,
"min": 0.66132400914,
"max": 0.81021430614,
"times": [
0.66132400914,
0.66970166414,
0.6659152511399999,
0.66182243214,
0.67061199314,
0.81021430614,
0.66824998714,
0.67752592914,
0.66991527314,
0.7936047161399999
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot storeResolution-only: cold packument cache (full re-resolve over the registry link) with a hot store (no tarball download), so this isolates pnpr offloading the client resolution to its warm server.
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 5.00716459532,
"stddev": 0.03560271028702763,
"median": 5.00775816462,
"user": 1.7942784599999997,
"system": 1.8772191799999998,
"min": 4.94588573112,
"max": 5.07472811512,
"times": [
4.98167067012,
4.94588573112,
4.98737537712,
5.01213191212,
5.00974720512,
5.07472811512,
5.03183452312,
4.98506777612,
5.0057691241199995,
5.03743551912
]
},
{
"command": "pacquet@main",
"mean": 5.060775184919999,
"stddev": 0.040828993588567807,
"median": 5.05267591512,
"user": 1.87439896,
"system": 1.90634898,
"min": 5.010615705119999,
"max": 5.117722307119999,
"times": [
5.010615705119999,
5.03132799412,
5.032192215119999,
5.10200987812,
5.08271029012,
5.117722307119999,
5.109882735119999,
5.07315961512,
5.01637072412,
5.03176038512
]
},
{
"command": "pnpr@HEAD",
"mean": 0.66092987922,
"stddev": 0.007385852101453611,
"median": 0.6599400321200001,
"user": 0.3349162600000001,
"system": 1.2490763800000002,
"min": 0.6516556371200001,
"max": 0.6730238931200001,
"times": [
0.6650987621200001,
0.6605767771200001,
0.6709843231200001,
0.6578063591200001,
0.6730238931200001,
0.6640203881200001,
0.6550232021200001,
0.6593032871200001,
0.6518061631200001,
0.6516556371200001
]
},
{
"command": "pnpr@main",
"mean": 0.67127406102,
"stddev": 0.012927603776531502,
"median": 0.6715642311200001,
"user": 0.34681625999999993,
"system": 1.2581707800000002,
"min": 0.6502537261200001,
"max": 0.69708442112,
"times": [
0.66445964812,
0.6502537261200001,
0.65618028112,
0.6722694221200001,
0.6808609881200001,
0.67532445612,
0.6707075651200001,
0.69708442112,
0.6747410621200001,
0.6708590401200001
]
}
]
} |
091feeb to
9e80124
Compare
The cold batch in `CreateVirtualStore` ran each per-snapshot `InstallPackageBySnapshot` as a future inside one cooperative `try_join_all` task, and each future called the *blocking* `CreateVirtualDirBySnapshot::run` (a `rayon::join` over CAS import + symlink layout) directly. Because all the futures share a single task, a blocking call in one prevents the others from being polled — so the 1308 slot-link operations on a fresh install serialized one-at-a-time, each paying its own `rayon::join` dispatch. The warm batch already avoids this by linking every slot in a single `rayon` `par_iter`. This was invisible on warm/restore installs (everything goes through the warm batch) but dominated the cold path: a pnpr client install, whose fast offloaded resolution routes every package through the cold batch, spent ~14.5s in `CreateVirtualStore` where an equivalent local fresh install — which pre-fills the store during its slower resolution and so hits the warm batch — spent ~5-7s linking. Defer the slot link out of the per-snapshot download future (new `InstallPackageBySnapshot::defer_link`) and run all the cold links in one parallel `rayon` pass after the downloads, mirroring the warm batch. On a fresh ~1300-package install through a pnpr server this cut `CreateVirtualStore` from ~14.5s to ~9.5s and the end-to-end install from ~18.5s to ~13s — now faster than the equivalent non-pnpr install (~15.5s), which it should be since the pnpr client offloads resolution and downloads less metadata. The progress events are unchanged in kind and order (`fetched`/`found_in_store` still fire during download, `imported` still fires once the slot is linked). --- Written by an agent (Claude Code, claude-opus-4-8).
9e80124 to
7966e73
Compare
Review Summary by QodoParallelize cold-batch slot linking to improve install performance
WalkthroughsDescription• Parallelize cold-batch slot linking in rayon pass after downloads • Defer slot link from per-snapshot futures to avoid serialization • Add defer_link flag to InstallPackageBySnapshot struct • Reduces CreateVirtualStore time by ~34% on fresh installs Diagramflowchart LR
A["Cold batch downloads"] -->|defer_link: true| B["Deferred slot links"]
B -->|rayon par_iter| C["Parallel link pass"]
C -->|isolated only| D["Virtual store slots"]
E["Warm batch"] -->|rayon par_iter| D
style C fill:#90EE90
style B fill:#FFB6C1
File Changes1. pacquet/crates/package-manager/src/create_virtual_store.rs
|
Problem
CreateVirtualStorehas two batches: a warm batch (packages already in the store) and a cold batch (packages it had to download). The warm batch links every slot in a singlerayonpar_iter— fast. The cold batch, however, ran each per-snapshotInstallPackageBySnapshotas a future inside one cooperativetry_join_alltask, and each future called the blockingCreateVirtualDirBySnapshot::run(arayon::joinover CAS import + symlink) directly.Because all those futures share a single task, a blocking call in one prevents the others from being polled — so on a fresh install the 1308 slot-links serialized one-at-a-time, each paying its own
rayon::joindispatch.This is invisible on warm/restore installs (everything is warm), but it dominates the cold path — which is exactly what a pnpr client install hits: its fast offloaded resolution means every package goes through the cold batch. Phase timing on a fresh ~1300-package install:
Same link work, ~2-3x slower, purely from the serialization.
Fix
Defer the slot link out of the per-snapshot download future (new
InstallPackageBySnapshot::defer_link) and run all the cold links in one parallelrayonpass after the downloads — mirroring the warm batch.Result
Fresh ~1300-package install through a pnpr server:
CreateVirtualStoreAnd end-to-end, pnpr now beats the equivalent non-pnpr install:
…which is the expected outcome, since the pnpr client offloads resolution and downloads ~half the metadata. Progress events are unchanged in kind and order (
fetched/found_in_storeduring download,importedonce the slot is linked).Tests
685
pacquet-package-manager+pacquet-clitests pass (incl. thepnpr_installintegration tests, which exercise the cold batch).cargo fmt+clippyclean.Related
Composes with #12248 (dedicated CAS-write pool), which removes a separate extraction↔link rayon-pool contention; both target the same materialize phase.
Written by an agent (Claude Code, claude-opus-4-8).
Summary by CodeRabbit
Refactor
Tests