Measurement Campaign Analysis¶
Archive status — 2026-10-07¶
This is a historical report. Measurements and decisions describe the original
run and revision; recommendations and commands are not current operating
instructions. Path corrections below change navigation, not measurements.
The original ignored report remains unchanged at .artifacts/analysis/2026-05-16-measurement-campaign-analysis.md
(SHA-256 889d1c6b48615376620be390e87b3ae50c3878d3283b4014eadfdc73aba4f60b).
Some original evidence is unavailable in this checkout. The missing paths are listed below; those observations have not been independently revalidated. Do not treat this archive as a fully reproducible current benchmark.
See the report archive index for coverage and remaining gaps.
| Artifact or input reference | Current availability |
|---|---|
.artifacts/20260502T172501Z |
unavailable historical path |
.artifacts/analysis/2026-05-03-measurement-campaign-analysis.md |
available |
.artifacts/benchmarks/backend/20260511T151057Z |
available |
Resolved directory moves:
.artifacts/20260511T151057Z→.artifacts/benchmarks/backend/20260511T151057Z.
Date: 2026-05-16
Hardware Snapshot¶
- Host kernel:
Linux verona 6.18.26-gentoo-dist #1 SMP PREEMPT_DYNAMIC Thu Apr 30 21:44:48 -00 2026 x86_64 - OS:
Gentoo Linux 2.18 - CPU:
Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz - CPU topology:
1 socket,6 physical cores,12 logical CPUs, max reported frequency4.70 GHz - Memory at analysis time:
46 GiBRAM total,83 GiBswap total - GPU: NVIDIA device is present historically on this host, but
nvidia-smifailed with:NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. - Embedding device environment:
CODIRA_EMBED_DEVICEwas unset - Embedding device selection in code:
src/codira/semantic/embeddings.py::_configured_embedding_device()returnscpuwhenCODIRA_EMBED_DEVICEis unset - Embeddings in this analysis should therefore be treated as CPU-backed
Compared Campaigns¶
- Current campaign:
.artifacts/benchmarks/backend/20260511T151057Z - Preceding campaign:
.artifacts/20260502T172501Z - Previous saved report examined:
.artifacts/analysis/2026-05-03-measurement-campaign-analysis.md
Metadata:
| Campaign | profile-summary.json version |
profile-summary.json commit |
Backend evidence |
|---|---|---|---|
.artifacts/20260502T172501Z |
1.21.7 |
2a5cbaec12ab3d9874257e8f8c34622120828c8f |
SQLite backend plugin only; indexes contain .codira/index.db |
.artifacts/benchmarks/backend/20260511T151057Z |
1.23.6.post1.dev1 |
d7c0182b83d0783b8a0a9f01f284c82ef0cacc5c |
DuckDB backend plugin present; indexes contain .codira/index.duckdb |
Artifact consistency note:
- The current campaign's
profile-summary.jsonrecords commitd7c0182b83d0783b8a0a9f01f284c82ef0cacc5c. - The current campaign's
*-index-phases.jsonfiles record commit8066db57535161dda316a70c1b00a89581bf3399. git show --no-patch 8066db57535161dda316a70c1b00a89581bf3399fails in the current repository withfatal: bad object.- The git-history comparison in this report therefore uses the commit recorded by
profile-summary.json, and the bad-object per-file metadata is treated as an artifact inconsistency.
Git history between the compared campaign commits:
36non-merge commits are present from2a5cbaetod7c0182.- The relevant changes include the DuckDB backend package and activation path:
d18b0bc feat(backend): scaffold duckdb backend package835d2d6 feat(backend): implement duckdb backend lifecycle0efb1f8 feat(backend): integrate duckdb activation path6a43f8d test(backend): add duckdb integration coverage9a7837b test(backend): stabilize duckdb validation surfaced237fca docs(backend): document duckdb integration2193b60 fix(backend): replace DuckDB savepoints with shared transactionsa46a9db fix(backend): restore DuckDB parity with SQLite index behaviorgit diff --stat 2a5cbae..d7c0182shows74 files changed, includingpackages/codira-backend-duckdb,src/codira/cli.py,src/codira/query/exact.py,src/codira/sqlite_backend_support.py,scripts/benchmark_index.py, anduv.lock.
Measurement note:
scripts/benchmark_index.pyrecordstimings.totalas wall-clock for one index pass.timings.embeddingsis cumulative diagnostic time across embedding hooks and is not additive tototal.- Hyperfine means are the primary top-level comparison surface.
Objective 1: Performance Evolution¶
Overall result:
- Comparable hyperfine measurements:
51 - Improved measurements:
7 - Regressed measurements:
44 - The current DuckDB campaign is substantially slower than the preceding SQLite campaign for indexing and for operations that trigger or inspect index state.
Aggregate by command family:
| Family | Previous mean sum | Current mean sum | Absolute delta | Delta % |
|---|---|---|---|---|
index --full |
3228.280s | 18369.857s | +15141.577s | +469.03% |
warm index |
20.635s | 2681.025s | +2660.391s | +12892.71% |
ctx |
114.001s | 1507.556s | +1393.555s | +1222.40% |
sym |
0.204s | 1.020s | +0.816s | +400.52% |
symlist |
0.198s | 0.383s | +0.185s | +93.44% |
audit |
0.184s | 0.295s | +0.111s | +60.21% |
calls |
0.190s | 0.285s | +0.095s | +50.12% |
emb |
5.733s | 5.863s | +0.130s | +2.27% |
caps |
0.176s | 0.182s | +0.005s | +3.08% |
plugins |
0.178s | 0.181s | +0.003s | +1.64% |
cov |
0.180s | 0.177s | -0.003s | -1.47% |
codira help |
0.148s | 0.149s | +0.000s | +0.22% |
Aggregate by repository:
| Repository | Previous mean sum | Current mean sum | Absolute delta | Delta % |
|---|---|---|---|---|
large-cpython |
1023.966s | 9990.529s | +8966.563s | +875.67% |
large-texlive |
1230.292s | 6566.661s | +5336.369s | +433.75% |
large-postgres |
691.771s | 4055.877s | +3364.107s | +486.30% |
medium-redis |
209.259s | 1042.796s | +833.537s | +398.33% |
small-codira |
51.166s | 230.289s | +179.123s | +350.08% |
small-fontshow |
42.489s | 203.491s | +161.002s | +378.92% |
small-tree-sitter-c |
20.056s | 151.777s | +131.721s | +656.78% |
small-tree-sitter-python |
16.127s | 142.162s | +126.036s | +781.53% |
small-requests |
19.255s | 64.669s | +45.415s | +235.86% |
medium-ohmyzsh |
12.327s | 41.048s | +28.720s | +232.98% |
medium-official-images |
12.759s | 28.254s | +15.495s | +121.44% |
small-nvm |
11.566s | 18.471s | +6.905s | +59.71% |
large-cldr-json |
22.308s | 23.824s | +1.517s | +6.80% |
small-dataset-json-examples |
6.016s | 5.985s | -0.031s | -0.52% |
Largest regressions:
| Benchmark | Previous | Current | Absolute delta | Delta % | Signal |
|---|---|---|---|---|---|
large-cpython index --full |
992.861s | 7166.569s | +6173.709s | +621.81% | 40.39 sigma |
large-texlive index --full |
1213.447s | 5993.477s | +4780.030s | +393.92% | 50.91 sigma |
large-postgres index --full |
679.101s | 3578.159s | +2899.058s | +426.90% | 59.17 sigma |
large-cpython warm index |
7.158s | 1406.628s | +1399.470s | +19551.53% | 30.27 sigma |
large-cpython ctx |
23.947s | 1417.332s | +1393.384s | +5818.52% | 28.14 sigma |
medium-redis index --full |
201.274s | 887.202s | +685.928s | +340.79% | 71.93 sigma |
large-texlive warm index |
3.877s | 559.561s | +555.685s | +14334.69% | 34.13 sigma |
large-postgres warm index |
3.410s | 469.131s | +465.721s | +13656.25% | 14.77 sigma |
medium-redis warm index |
1.173s | 148.840s | +147.668s | +12589.17% | 13.53 sigma |
small-codira index --full |
38.587s | 178.613s | +140.026s | +362.89% | 16.92 sigma |
Measured improvements:
| Benchmark | Previous | Current | Absolute delta | Delta % | Signal |
|---|---|---|---|---|---|
large-cldr-json ctx |
10.567s | 8.453s | -2.114s | -20.01% | 6.61 sigma |
large-postgres ctx |
9.260s | 8.587s | -0.673s | -7.26% | 2.09 sigma |
small-dataset-json-examples ctx |
5.599s | 5.235s | -0.364s | -6.49% | 1.70 sigma |
small-tree-sitter-c ctx |
5.732s | 5.382s | -0.350s | -6.11% | 1.47 sigma |
small-nvm ctx |
5.435s | 5.282s | -0.153s | -2.81% | 0.72 sigma |
medium-redis ctx |
6.812s | 6.753s | -0.059s | -0.87% | 0.20 sigma |
small-codira cov |
0.180s | 0.177s | -0.003s | -1.47% | 0.18 sigma |
Index phase deltas:
| Phase | Previous | Current | Absolute delta | Delta % |
|---|---|---|---|---|
large-cpython total |
938.897s | 6909.322s | +5970.425s | +635.90% |
large-cpython indexing |
882.683s | 6851.452s | +5968.769s | +676.21% |
large-texlive indexing |
1160.404s | 6036.035s | +4875.631s | +420.17% |
large-texlive total |
1194.427s | 6069.347s | +4874.920s | +408.14% |
large-postgres total |
670.451s | 3564.978s | +2894.527s | +431.73% |
large-postgres indexing |
657.139s | 3550.851s | +2893.712s | +440.35% |
medium-redis total |
196.937s | 909.916s | +712.978s | +362.03% |
medium-redis indexing |
192.638s | 905.261s | +712.623s | +369.93% |
large-cpython embeddings |
1730.891s | 2096.966s | +366.075s | +21.15% |
large-texlive embeddings |
2299.139s | 2633.243s | +334.104s | +14.53% |
Conclusion for Objective 1:
- The current campaign is dominated by a severe DuckDB indexing regression compared with the preceding SQLite campaign.
- The strongest regression is in the measured
indexingphase, not in parsing, discovery, or scanner filtering. - CPU embeddings remain expensive in absolute terms, but they do not explain the largest regression: for
large-cpython,indexingregressed by+5968.769s, whileembeddingsregressed by+366.075s. - The few improvements are concentrated in selected
ctxruns and are not large enough to offset the indexing regression.
Objective 2: Hotspots And Sensitive Areas¶
Hotspots in importance order:
- DuckDB persistence/indexing path
Evidence:
index --fullregressed by+15141.577saggregate.- The
indexingphase alone regressed by: large-cpython:+5968.769slarge-texlive:+4875.631slarge-postgres:+2893.712smedium-redis:+712.623s- Current
large-cpython-index.profis dominated by import-system work: <frozen importlib._bootstrap>:_find_spec:6829.405s,28869132calls<frozen importlib._bootstrap_external>:find_spec:6116.494s,28869122calls<frozen importlib._bootstrap_external>:_get_spec:6093.143s,28869122calls<frozen importlib._bootstrap_external>:find_spec:5601.740s,346392454calls- The preceding SQLite
large-cpython-index.profdid not have this importlib pattern; its top entries were_flush_embedding_rowsandembed_texts. - Current
large-cpythonindex storage size is2.8Gfor.codira/index.duckdbversus462Mfor the preceding SQLite.codira/index.db.
Relevant code areas:
packages/codira-backend-duckdb/src/codira_backend_duckdb/__init__.py::DuckDBIndexBackend.persist_analysispackages/codira-backend-duckdb/src/codira_backend_duckdb/__init__.py::DuckDBConnection.executepackages/codira-backend-duckdb/src/codira_backend_duckdb/__init__.py::DuckDBConnection.executemanypackages/codira-backend-duckdb/src/codira_backend_duckdb/__init__.py::_duckdb_lastrowidsrc/codira/sqlite_backend_support.py::_flush_embedding_rows-
src/codira/sqlite_backend_support.py::_insert_symbol_index_row -
DuckDB warm-index and automatic index-refresh path
Evidence:
- Warm
indexregressed by+2660.391saggregate. - Largest warm-index regressions:
large-cpython:+1399.470slarge-texlive:+555.685slarge-postgres:+465.721smedium-redis:+147.668slarge-cpython ctxregressed by+1393.384s, nearly the same order aslarge-cpython warm index.
Relevant code areas:
src/codira/cli.py::_ensure_indexsrc/codira/cli.py::_run_locked_index_refreshsrc/codira/indexer.py::index_repo-
backend
load_existing_file_hashes()and runtime inventory paths inherited or adapted by the DuckDB backend -
CPU embedding inference
Evidence:
- Current full-index embedding diagnostic time remains high:
large-cpython:2096.966slarge-texlive:2633.243slarge-postgres:1410.031smedium-redis:424.420sCODIRA_EMBED_DEVICEis unset and_configured_embedding_device()defaults tocpu.nvidia-smicannot communicate with the NVIDIA driver, so GPU acceleration is not available in this environment.
Relevant code areas:
src/codira/semantic/embeddings.py::embed_textssrc/codira/semantic/embeddings.py::_configured_embedding_device-
src/codira/sqlite_backend_support.py::_flush_embedding_rows -
Semantic retrieval and vector scoring
Evidence:
- The preceding report identified SQLite semantic retrieval and Python-side
_dot_similarityas a largectxhotspot. - Current DuckDB
large-cpython-ctx.profincludes: codira_backend_duckdb.execute:9.382ssrc/codira/query/context.py::_retrieve_embedding_candidates:9.362ssrc/codira/query/producers.py::retrieve_candidates:9.362s- This is no longer the largest current regression because the warm-index/refresh path dominates
large-cpython ctx, but it remains performance-sensitive.
Relevant code areas:
src/codira/query/context.py::_retrieve_embedding_candidatessrc/codira/query/producers.py::retrieve_candidates-
backend
embedding_candidates()implementations -
Small query surfaces under DuckDB
Evidence:
small-codira sym:0.204sto1.020s,+0.816s,+400.52%small-codira symlist:0.198sto0.383s,+0.185s,+93.44%small-codira audit:0.184sto0.295s,+0.111s,+60.21%small-codira calls:0.190sto0.285s,+0.095s,+50.12%
Relevant code areas:
- DuckDB query adapter and inherited SQLite query helpers
src/codira/query/exact.pysrc/codira/query/graph_enrichment.py-
backend
docstring_issues,symbol_rows, and graph query methods -
JSON analyzer acceptance path
Evidence:
- Current
large-cldr-json ctximproved by-2.114sand-20.01%. - Current
large-cldr-jsontotal aggregate regressed only+1.517s, much smaller than Python/C/TeX workloads. - The preceding report explicitly benchmarked and rejected the accepted-file double-load follow-up because only
50files were accepted andanalyze_file()across accepted files took about0.005stotal.
Conclusion:
- JSON remains content-sensitive, but the previous no-go decision still stands. No new fact in the current campaign invalidates it.
Objective 3: Performance Enhancement Plan¶
Assessment on current hardware:
- There is significant performance headroom.
- The dominant current problem is not hardware saturation; it is a DuckDB backend implementation/regression problem.
- Heavy refactoring can produce large gains if DuckDB gets a backend-native persistence path instead of relying on SQLite-shaped compatibility behavior.
Plan in importance order:
- Make DuckDB full-index persistence backend-native.
Scope:
- Instrument
DuckDBConnection.execute,DuckDBConnection.executemany,_duckdb_lastrowid,DuckDBIndexBackend.persist_analysis, and shared_store_analysiscall sites. - Add a focused DuckDB-vs-SQLite persistence benchmark on a fixed fixture and one medium real corpus.
- Replace row-by-row SQLite-shaped persistence where it dominates with DuckDB-oriented batch inserts.
- Avoid per-row sequence round trips where possible by allocating IDs deterministically or using DuckDB features that return inserted IDs without a separate
currval()query per row. - Keep SQLite behavior unchanged.
Expected gain:
- Measured full-index regression upper bound:
+15141.577saggregate across the campaign. - A reasonable first target is reclaiming
50%to80%of the DuckDB-specific indexing overhead after profiling confirms the exact frame-level cause. - On the largest measured workloads, that corresponds to approximate savings of:
large-cpython:3000sto4800slarge-texlive:2400sto3900slarge-postgres:1400sto2300s
Risk:
-
This is a medium-to-heavy backend refactor because it touches persistence semantics and must preserve deterministic row identity and query parity.
-
Fix DuckDB warm-index and
ctxrefresh regressions.
Scope:
- Profile warm
codira indexseparately from full index on the current DuckDB artifacts. - Verify whether the warm path is spending time in file-hash loading, runtime inventory, analyzer inventory, derived-index rebuild, or unintended re-persistence.
- Add targeted timing around
load_existing_file_hashes(),_inspect_index_rebuild_request(),_ensure_index(), and backend connection setup. - Ensure unchanged files remain cheap under DuckDB and that
ctxdoes not pay a near-full refresh cost.
Expected gain:
- Measured warm-index regression upper bound:
+2660.391saggregate. - Measured
large-cpython ctxregression:+1393.384s. - If the root cause is shared with the persistence path, this may fall out of action 1.
- If it is a separate freshness/read-path problem, a reasonable target is restoring warm
indexto within2xof the SQLite campaign for unchanged repositories.
Risk:
-
Medium. It may expose backend API gaps rather than a localized bug.
-
Keep vector-native semantic retrieval on the existing roadmap.
Scope:
- Replace brute-force vector retrieval and Python-side similarity scoring with an optional vector-native retrieval backend.
- Preserve deterministic exact retrieval as authoritative.
Expected gain:
- The preceding report estimated
15%to35%on large-repoctx. - That estimate remains plausible for semantic-heavy
ctx, but it is not the immediate worst offender in the current DuckDB campaign because warm-index/refresh dominates.
Risk:
-
Heavy architecture work. This should stay tied to the existing vector-backend issue rather than becoming an ad hoc local optimization.
-
Keep embedding runtime and calibration work on the existing roadmap.
Scope:
- Use hardware-aware calibration for CPU/GPU/thread/batch choices.
- Consider a long-lived embedding runtime only as part of daemon or runtime-lifetime work.
Expected gain:
- Current CPU embedding diagnostic time is still high:
2096.966sonlarge-cpython,2633.243sonlarge-texlive. - Previous estimate remains
15%to30%for large full indexes if runtime/backend changes are effective. - On this host, GPU-based expectations should not be used until
nvidia-smiworks andCODIRA_EMBED_DEVICEis explicitly configured.
Risk:
-
Medium to heavy. It crosses configuration, hardware detection, and runtime lifecycle.
-
Defer small DuckDB query micro-regressions until actions 1 and 2 are complete.
Scope:
- Re-measure
sym,symlist,audit, andcallsafter DuckDB persistence/read-path fixes. - Optimize adapter/query paths only if micro-regressions remain.
Expected gain:
- Absolute wins are below
1sper command in the current benchmark matrix. -
This is not first-order work while full-index and warm-index regressions are measured in hundreds or thousands of seconds.
-
Do not reopen the JSON accepted-file double-load optimization.
Reason:
- The previous report benchmarked that idea and explicitly rejected it.
- The current campaign does not provide a substantially new fact that invalidates that decision.
large-cldr-json ctximproved by20.01%in the current comparison.
Objective 4: Roadmap Snapshot Cross-Reference¶
Snapshot validation:
issues.jsonexists and parses.milestones.jsonexists and parses.issues.json:data.repository.issues.totalCount = 17,hasNextPage = false.milestones.json:data.repository.milestones.totalCount = 5,hasNextPage = false.- Nested milestone issue lists all have
hasNextPage = false. - Conclusions in this section are based only on the local snapshot files.
Existing open issues related to Objective 3:
| Objective 3 area | Open issue relation | Match quality | Reason |
|---|---|---|---|
| Vector-native semantic retrieval | #20 Feature: Introduce Optional Vector Database Backend for Semantic Retrieval |
Strong | Issue body directly targets optional vector-database-backed semantic retrieval. |
| Embedding runtime lifetime | #22 Feature: Daemon Mode for Incremental File Watching and Automatic Reindexing |
Partial | Daemon mode can keep process/runtime state warm, but the issue is broader than embedding inference. |
| Embedding hardware tuning | #28 feat(embeddings): Add embeddings calibration script (hardware-aware auto-tuning) |
Strong for calibration | Issue body explicitly covers device selection, thread count, batch size, and GPU memory limits. |
| Configuration needed for tuning | #17 Introduce install-time configuration system for Codira (hardware + plugins) and #27 Introduce configuration injection in core <-> plugin interface |
Supporting | These issues provide configuration surfaces, not direct performance fixes. |
Closed or not-open issue check:
- The local open issue snapshot does not list issue
#10. git log --grep=duckdbshows the DuckDB backend implementation and follow-up fixes already landed.docs/process/issue-010-duckdb-backend.mdrecords issue#10scope as backend implementation, activation, and SQLite observable parity.- The current DuckDB performance regression is therefore not covered by an open issue in the local snapshots, and issue
#10should not be treated as an open performance task.
Completely new points relative to the open roadmap:
- DuckDB full-index persistence performance parity.
- DuckDB warm-index and automatic refresh performance parity.
- DuckDB exact/query adapter micro-regression cleanup, after the large backend regressions are fixed.
Actions that should immediately increase performance, excluding actions already covered by open issues:
- Create and implement a DuckDB persistence performance task.
Detailed plan:
- Add a targeted benchmark fixture that runs the same index workload with
CODIRA_INDEX_BACKEND=sqliteandCODIRA_INDEX_BACKEND=duckdb. - Instrument DuckDB persistence frames:
DuckDBIndexBackend.persist_analysisDuckDBConnection.executeDuckDBConnection.executemany_duckdb_lastrowid- shared
_store_analysisinsert helpers - Confirm whether importlib churn comes from DuckDB driver calls, adapter error-class lookup, parameter binding, or another backend-local path.
- Replace the confirmed slow path with DuckDB-native batch persistence.
- Validate with:
- identical indexed/reused/deleted/failed counts
- equivalent query results for
sym,ctx,calls, andcov - benchmark comparison against
.artifacts/benchmarks/backend/20260511T151057Z
Expected immediate gain:
- Highest expected gain among new actions.
-
Target: reclaim at least half of the measured DuckDB full-index overhead before broader backend work continues.
-
Create and implement a DuckDB warm-index/readiness performance task.
Detailed plan:
- Benchmark warm
codira indexon one small, one medium, and one large corpus. - Add phase-level timing for:
- existing file hash loading
- runtime inventory loading
- analyzer inventory loading
- derived-index rebuild
- connection open/close
- Confirm that unchanged files do not flow through expensive persistence or embedding paths.
- Fix the first confirmed warm-path bottleneck.
- Add a regression benchmark or focused test that proves an unchanged DuckDB index has bounded warm-index behavior.
Expected immediate gain:
- Very high if it restores warm indexing close to the previous SQLite campaign.
-
Upper bound from measured campaign:
+2660.391saggregate warm-index regression. -
Re-measure and then clean up DuckDB small-query overhead.
Detailed plan:
- Run
sym,symlist,audit,calls, andcovagainst the same DuckDB and SQLite small-codira indexes. - Attribute the remaining overhead after actions 1 and 2.
- Optimize adapter or query SQL only for measured remaining regressions.
Expected immediate gain:
- Low in absolute campaign time, but useful for CLI responsiveness.
- Current measured upper bound is under
1sper command for the small-codira query set.
Actions explicitly not proposed here:
- Do not propose JSON accepted-file double-load reuse. It was evaluated and discarded in the previous report, and the current campaign does not invalidate that decision.
- Do not propose vector-native retrieval as a new action. It is already covered by open issue
#20. - Do not propose embedding calibration as a new action. It is already covered by open issue
#28. - Do not propose daemon/runtime lifetime as a new action. It is already partially covered by open issue
#22.
Final Assessment¶
The current campaign should be treated as a failed DuckDB performance gate, not as a general Codira slowdown. The strongest evidence is the jump in full-index and warm-index times after the DuckDB backend landed, the presence of .codira/index.duckdb in the current campaign indexes, and the phase-level concentration in indexing.
There is no basis for saying the current hardware prevents significant gains. The first performance work should target DuckDB persistence and warm-index behavior before returning to broader semantic retrieval or embedding-runtime projects.