Skip to content

perf(index): remove synchronous preamble indexing from cold start #606

Description

@16bit-ykiko

Ledger of the cold-start / index-cost investigation from the benchmark-baseline round (tooling in #605). Numbers from Linux x86_64, RelWithDebInfo, minimum of repeated runs; reproduce with the recipes in benchmarks/README.md.

Cold-start account (small TU, <string> + <vector> preamble)

client didOpen -> first diagnostics   ~440ms
= PCH compile                          260ms   (clangd --check preamble: 220ms)
+ preamble index + links + blob         82ms   (clangd: deferred to background)
+ PCH disk flush                         2ms
+ main-file AST over the PCH            41ms   (clangd "Building AST": ~46ms)
+ two IPC hops + master scheduling      ~8ms

The PCH compile itself is at parity with clangd modulo TokenBuffer collection (~+40ms; preprocessing alone measures 18ms -> 32ms with tokens on). The TokenBuffer is consumed by the preamble indexing (the Semantics table reads expanded tokens), so both extra costs are two faces of one design decision: clice indexes the preamble synchronously inside the cold-start critical path; clangd does not. On heavy TUs the same structure scales to seconds (PCH build 2-5s, index share 15-25%).

Transport and copies are clean on this path: the PreambleState blob is written to disk by the worker and never crosses IPC (the master receives paths + diagnostics, KB-sized), and the whole pipeline residual is ~8ms. Warm requests: worker-side 0.11ms vs client-observed 0.74ms, ~0.6ms per round trip.

Index pass internals ([perf:index_detail], run any workload with pipeline_benchmark --log-level info)

Heavy TU (src/semantic/semantics.cpp, ~6.4s parse):

TUIndex::build            650ms  = semantics table  82-158ms
                                 + projection      500-530ms   <- dominant
                                 + include graph + dedup  70ms
serialize                 137ms  = path-rekeying copy 11-17ms
                                 + flatbuffers pack  126-155ms  <- dominant
  • Projection is the index hot spot (~75-80% of the build): walking semantics rows and emitting occurrences/relations per file. Any index-build optimization starts here.
  • The suspected "accidental copy" exists but is small: TUIndex::serialize deep-copies every FileIndex while rekeying FileID -> path_id (path_file_indices[...] = file_index). Measured 11-17ms on a heavy TU (~2% of the pass). Worth folding into a move once the serialize-twice contract is settled; not a priority.
  • flatbuffers pack dominates serialization (~90%). Splitting builder-build vs finalize would need instrumentation inside kotatsu's codec; only worth it if this line ever becomes the bottleneck.

Directions (decisions pending, in rough order of leverage)

  1. Move preamble indexing off the critical path. Build the PCH, answer diagnostics, index asynchronously; index-backed features over preamble headers degrade until ready. Theoretical cold open lands at clangd's line (~270ms small TU). Needs a design for the degradation window and for TokenBuffer (only collected when the async indexer runs).
  2. Skip re-indexing headers whose index is already on disk. A preamble covers thousands of headers; stdlib and third-party headers are rarely macro-sensitive in practice. If a header's content hash (and macro context) matches an existing shard, its projection can be skipped entirely. Interacts with the shard/content-hash model — needs its own design round.
  3. Projection micro-optimization — profile inside project_semantics before touching anything.

Instrumentation for all of the above ships in #605 ([perf:build], [perf:index_detail], pipeline_benchmark, clangd --check recipe).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature:indexSymbol indexing and cross-TU featureskind:performanceSlowness, high memory or CPU usageos:linuxHappens on LinuxtriagedProcessed by triage — excluded from future triage runs

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions