Skip to the content.

Notes 2026/07/29: RTS performance in Comet v2026.02.1

See notes/2024100_FI for the original Comet-FI notes and notes/20260610_FI for v2026.02.0 notes on Comet-FI.

These reported search times/speeds are the theoretical maximums. Previously reported throughput times were slightly affected by file I/O spectrum reading, dampening the reported search speeds. Reported times below are based on pre-loading all spectra into memory so that search throughput is not gated by the slower file parsing.

All timings are run on a Dell Windows 11 computer with 64GB RAM and an Intel Core Ultra 7 265K processor.

From an RTS perspective, the v2026.02.1 release of Comet added performance optimizations to the old peptide index format. This includes multithread support (running concurrent searches) and using the fragment-ion index (FI) peptide generation (and modification permutation) code for optimized index file generation. In order to support concurrent PI searches, the peptide index is now loaded into memory. There is a memory cost associated with loading the peptide index in memory but it’s significantly less than the corresponding FI search.

Real-time search (RTS) throughput and latency for Comet’s two index backends: the fragment-ion index (FI) and the peptide index (PI), searched against a human canonical target/decoy FASTA with variable oxidized methionine and phospho-STY, at 1, 2, 4, 8, and 20 search threads.

Comet RTS peptide index vs. fragment ion index performance:

Search
threads
Avg. search time (ms) Avg. search speed (Hz) Peak memory (GB)
PI FI PI FI PI FI
113.250.74751,35611.720.2
26.790.381472,65111.720.2
43.400.192945,20911.820.3
81.820.115488,99111.920.4
200.790.081,25912,68112.220.7

Per-run full summary statistics

Scroll horizontally to see all columns →

IndexThreadsSpectraAvg msHz Max ms %≤1ms%≤5ms%>10msMem GB Init sPreload sSearch sTotal s
FI163,4880.741,35617.096.67%99.96%0.02%20.264.44.0246.8115.5
FI263,4880.382,65121.096.37%99.93%0.03%20.264.64.0624.092.9
FI463,4880.195,20920.096.20%99.91%0.03%20.364.44.2012.281.1
FI863,4880.118,99124.093.35%99.79%0.09%20.465.44.097.176.9
FI2063,4880.0812,68132.074.42%97.88%0.35%20.765.04.145.074.5
PI163,48813.2575406.011.15%48.64%29.72%11.730.34.36841.5876.4
PI263,4886.79147420.010.53%47.28%30.65%11.730.33.94431.1465.7
PI463,4883.40294422.010.32%46.73%30.93%11.829.64.27215.8250.0
PI863,4881.82548417.08.43%41.72%34.25%11.929.54.04115.9149.7
PI2063,4880.791,259459.07.87%38.40%37.64%12.230.04.0050.484.7

Comparison of release v2026.02.0 vs v2026.02.1 in both batch mode and RTS

The RTS results above show the peptide-index (PI) and fragment-ion-index (FI) backends within a single v2026.02.1 build. The two comparisons below instead hold the index backend fixed (FI, batch/offline mode, not RTS) and compare the previous release, v2026.02.0, against v2026.02.1, to see what the v2026.02.1 batch-search optimizations were worth in practice. No AScorePro localization is applied in these following v2026.02.0 vs v2026.02.1 comparisons.

Metric v2026.02.0 v2026.02.1
Index build time 14s 15s
Index build peak memory 2.6 GB 3.2 GB
Search total time (incl. FI generation) 2m40s 1m25s
  — FI generation sub-time 1m0s 1m2s
  — actual search phase 1m35s (661 Hz) 18s (3,392 Hz)
Search peak memory 19.7 GB 20.6 GB
PSMs — XCorr, 1% FDR 17,569 17,740
PSMs — XCorr, 5% FDR 23,748 23,871
PSMs — E-value, 1% FDR 20,519 20,545
PSMs — E-value, 5% FDR 25,761 25,842
Metric v2026.02.0 v2026.02.1
Index build time 10s 9s
Index build peak memory 1.9 GB 1.8 GB
Search time (incl. FI generation from .idx) 54s 51s
  — FI generation sub-time 41s 39s
  — actual search phase 11s (4,382 Hz) 10s (4,881 Hz)
Search peak memory 13.0 GB 14.0 GB
PSMs — XCorr, 1% FDR 16,352 16,428
PSMs — XCorr, 5% FDR 19,789 19,874
PSMs — E-value, 1% FDR 17,681 17,757
PSMs — E-value, 5% FDR 20,628 20,625

Why v2026.02.1 is so much faster here

Comet PR #115, “Batch search FI optimization,” changed how the FI backend processes a batch search. The old approach (still used in v2026.02.0) ran in three separate sweeps over all spectra: preprocess everything, then search everything, then post-process everything, with each sweep reading its intermediate results back out of main memory. v2026.02.1 instead fuses that into a single per-spectrum pipeline, where each worker thread carries one spectrum all the way through preprocessing, searching, scoring, E-value calculation, delta-Cn, and AScore in one pass, using scratch buffers that stay local to that thread.

Three follow-up commits tightened that same fused pipeline further:

Why Run 1 shows a much bigger speedup than Run 2: the fused pipeline’s advantage comes from cutting out round-trips through memory and per-spectrum heap allocation — overhead that grows with how much work each spectrum requires. Run 1’s parameters (3 modifications per residue type, 3 total) build a much larger FI index (roughly 3.6–3.7 billion entries, versus about 2.3 billion for Run 2) and do substantially more candidate matching per spectrum, so the old three-sweep approach’s overhead adds up much faster there. Run 2’s narrower search does less work per spectrum, so both versions’ per-spectrum overhead is small enough that the architectural difference barely shows up (4,382 Hz vs. 4,881 Hz).

Bottom line: the roughly 5x FI batch-search speedup seen in Run 1 for v2026.02.1 is expected, and it’s the direct, intended result of the optimization work in PRs #115, #119, and #120 — not a bug or inconsistency in either version.

RTS-mode FI search: v2026.02.0 vs v2026.02.1

The comparison above drives Comet’s batch search engine (comet.exe). This section repeats the same v2026.02.0-vs-v2026.02.1 comparison, but driving the real-time-search API instead (RealtimeSearch.exe, the same console harness used to generate the PI-vs-FI table earlier in this page), against the exact same two .raw files and the exact same FI indexes as Run 1 and Run 2 above — this time swept across the same 1/2/4/8/20 search-thread counts used in that first table. AScorePro localization was switched off in both versions so that neither run was doing extra scoring work the other wasn’t.

One methodology note: v2026.02.1’s RTS harness measures its own average search speed differently than v2026.02.0’s does internally, so the two versions’ self-reported Hz numbers aren’t directly comparable to each other. To keep the comparison fair, the “search phase” times and speeds below are instead computed the same way for both versions: total wall-clock run time, minus the one-time fragment-ion-index generation step that happens once at startup.

Scroll horizontally to see all columns →

Search
threads
FI generation (s) Search phase (s) Search speed (Hz) Peak memory (GB) PSMs, XCorr 1% FDR PSMs, E-value 1% FDR
v0v1 v0v1 v0v1 v0v1 v0v1 v0v1
162.762.564.148.39901,31421.420.217,84717,74220,44420,530
262.764.038.326.81,6572,36721.520.217,84717,74220,44520,530
464.262.424.816.22,5563,92621.420.317,84717,74220,44620,530
863.264.513.710.34,6416,15222.920.417,84717,74220,44520,530
2063.762.412.99.34,9146,81223.520.717,84717,74220,44620,530

Scroll horizontally to see all columns →

Search
threads
FI generation (s) Search phase (s) Search speed (Hz) Peak memory (GB) PSMs, XCorr 1% FDR PSMs, E-value 1% FDR
v0v1 v0v1 v0v1 v0v1 v0v1 v0v1
141.141.749.940.49991,23314.212.916,83416,42817,66417,757
241.440.827.322.01,8292,26814.312.916,83416,42817,68517,757
441.340.716.113.13,0943,80814.913.016,83416,42817,66317,757
841.341.610.59.34,7345,38915.513.116,83416,42817,66317,757
2041.040.88.57.25,8856,95216.413.316,83416,42817,68517,757

(v0 = v2026.02.0, v1 = v2026.02.1, colored to match the PI/FI palette used above — that’s purely a color reuse, not a claim about index backend.)

Isolating pure search-call time from spectrum reading

The tables above measure wall-clock time for the whole per-scan loop each worker thread runs, which is the honest end-to-end throughput of this benchmark harness — but RTS itself is properly defined as “submit a spectrum, get results back” (DoSingleSpectrumSearchMultiResults), and that call’s own runtime shouldn’t depend on how or when the spectrum’s peak arrays were read from a file. Since this harness reads spectra out of a static .raw file to simulate real-time arrival (rather than receiving them from a live instrument feed), it’s worth asking how much of the gap above is the search engine itself getting faster, versus an artifact of how this particular tool simulates spectrum delivery.

Both versions’ C# harness already time-boxes exactly the search call, per scan, with a thread-local Stopwatch that never touches shared state (RealtimeSearch/SearchMS1MS2.cs) — so summing those durations and dividing by thread count gives an estimate of pure search-call throughput, with all spectrum-reading time excluded entirely. v2026.02.0 already computes and prints exactly this (its self-reported “MS2 average search time”/Hz). v2026.02.1 had switched to reporting true wall-clock time instead, so it didn’t have this number — a few lines were added to SearchMS1MS2.cs to accumulate the same sum-of-calls figure there too, and both versions were rerun across the full thread sweep with that instrumentation in place.

Scroll horizontally to see all columns →

Search
threads
Run 1 (wide mods)
full wall-clock Hz
Run 1 (wide mods)
pure search-call Hz
Run 2 (narrow mods)
full wall-clock Hz
Run 2 (narrow mods)
pure search-call Hz
v0v1 v0v1 v0v1 v0v1
19901,3141,3631,4129991,2331,3001,351
21,6572,3672,6182,7691,8292,2682,5472,606
42,5563,9264,4945,0943,0943,8084,6595,021
84,6416,1527,1638,7174,7345,3897,1058,849
204,9146,8129,68610,8645,8856,9529,20313,162

Turning those into v2026.02.0→v2026.02.1 percentage deltas makes the pattern clearer:

Search threads Run 1 wall-clock Δ Run 1 pure search-call Δ Run 2 wall-clock Δ Run 2 pure search-call Δ
1 +32.7% +3.6% +23.4% +3.9%
2 +42.8% +5.8% +24.0% +2.3%
4 +53.6% +13.4% +23.1% +7.8%
8 +32.6% +21.7% +13.8% +24.5%
20 +38.6% +12.2% +18.1% +43.0%

This changes the picture in an important way. At low thread counts, most of the gap disappears once I/O is excluded: Run 1 at 1 thread goes from +32.7% (full wall-clock) down to +3.6% (pure search-call); Run 2 at 2 threads goes from +24.0% down to +2.3%. That confirms the suspicion behind this section — a good chunk of the originally-reported speedup was this benchmark harness’s read strategy, not the search engine getting faster, and in a live deployment (where spectra arrive already-parsed from the instrument’s own acquisition callback, with no shared file reader for threads to contend over) that portion of the gap likely wouldn’t appear at all.

But a real, thread-count-dependent gap remains even with I/O fully excluded, and it grows at higher concurrency: +21.7% (Run 1) / +24.5% (Run 2) at 8 threads, +12.2% / +43.0% at 20 threads — versus single digits at 1 thread. A gap that scales up with thread count, inside a timer that only wraps the search call itself, points at contention inside the search engine’s own threaded code, not at file I/O. That lines up with two fixes already covered in the batch comparison further up this page, both of which touch the same per-spectrum scoring path RTS calls into: c639d8be (removed a shared-mutex bottleneck in result hand-off) and 59b0f000 (replaced per-spectrum heap alloc/free of the sparse XCorr matrix with a per-thread bump allocator). Mutex contention and heap-allocator contention both get worse as more threads compete for them, which is exactly the pattern in this table — so this residual gap looks like a genuine core-engine improvement, not a benchmark artifact.

Revised bottom line: the original “same underlying idea as batch’s I/O decoupling” explanation for RTS’s speedup was only half the story. Read strategy (preload vs. interleaved-with-search) explains most of the gap at low thread counts; a real, separate engine-level fix (shared-mutex and allocator contention, worse at high thread counts) explains the rest, more of it at 8–20 threads. Neither of those was captured by looking at full wall-clock time alone.

Peak memory tells a different story from batch, worth calling out rather than glossing over: batch’s peak memory went up slightly from v2026.02.0 to v2026.02.1 (its fused pipeline holds a thread-local scratch buffer per thread), while RTS’s peak memory went down by 1–3 GB in both runs here, and the gap barely moves across the thread sweep. That’s consistent with v2026.02.1’s RTS harness trading a large “read every scan up front” preload buffer for whatever per-scan allocation pattern v2026.02.0 used, but this hasn’t been traced through the allocator code directly, so treat it as an observation rather than a confirmed root cause.

XCorr-based PSM counts are exactly thread-count-invariant within each version (17,847 for every v2026.02.0 row in Run 1, 17,742 for every v2026.02.1 row, and likewise 16,834 / 16,428 in Run 2) — as they should be, since thread count only changes how fast spectra are processed, never which peptide wins. E-value-based counts are effectively the same story, but with a small (≤0.15%, ~20 PSMs) run-to-run wobble visible only in v2026.02.0 (20,444–20,446 in Run 1, 17,663–17,685 in Run 2, depending on thread count); v2026.02.1 comes out perfectly identical across all five thread counts in both runs. That’s a nice independent confirmation of the RTS e-value determinism fix described elsewhere in this repo’s history: the older version still shows the residual thread-count-linked jitter, the current version doesn’t.

PSM counts also track the batch numbers closely (e.g. Run 1, XCorr at 1% FDR: 17,847/17,742 PSMs here for RTS v2026.02.0/v2026.02.1, versus 17,569/17,740 for batch in the same two versions above), and the small differences between RTS and batch, or between v2026.02.0 and v2026.02.1 within RTS, are all within the same low-single-digit-percent range seen everywhere else in this document — not evidence of a correctness problem in either version.