mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-08 08:53:43 +08:00
feat(diagnostics): report per-file cross-LSP registry cost
The per-file cross-LSP path rebuilds a type registry for every file. Whether
that is cheap or quadratic depends entirely on how many defs the module
filter leaves, and nothing reported it — so a filter that reduces by a
constant FACTOR rather than to a constant SIZE looked identical to one that
worked.
parallel.resolve.perfile_registry reports defs_per_file next to defs_total,
plus how often the filter failed. Measured on one Java tree:
1/4 corpus: defs_total=179033 defs_per_file=1292
full corpus: defs_total=689216 defs_per_file=5031
defs_per_file grew 3.89x while the corpus grew 3.85x — lockstep, so per-file
work is proportional to the whole corpus and cross-file LSP is
O(files x corpus_defs). filter_failed=0 throughout, which is why this was
invisible: the filter always 'succeeds', it just does not bound the set.
Diagnostics only; no behaviour change.
Refs #1669.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ── Constants ─────────────────────────────────────────────────── */
|
||||
@@ -1001,6 +1002,25 @@ void cbm_pxc_run_one_ts(CBMFileResult *r, const char *source, int source_len, co
|
||||
* `rust_shared_get` supplies the lazily-built shared Rust all-defs registry
|
||||
* (the parallel resolver owns its once-guard); NULL means "no shared rust
|
||||
* registry available" and rust NULL-filter files take the per-file build. */
|
||||
/* Per-file registry-build cost counters (#1669). Surfaced by pass_parallel at
|
||||
* end of resolve. */
|
||||
_Atomic uint64_t g_pxc_defs_registered = 0;
|
||||
_Atomic uint64_t g_pxc_build_files = 0;
|
||||
_Atomic uint64_t g_pxc_filter_files = 0;
|
||||
_Atomic uint64_t g_pxc_filter_failed = 0;
|
||||
|
||||
void cbm_pxc_filter_stats(uint64_t *defs_registered, uint64_t *build_files, uint64_t *filter_files,
|
||||
uint64_t *filter_failed) {
|
||||
if (defs_registered)
|
||||
*defs_registered = atomic_load_explicit(&g_pxc_defs_registered, memory_order_relaxed);
|
||||
if (build_files)
|
||||
*build_files = atomic_load_explicit(&g_pxc_build_files, memory_order_relaxed);
|
||||
if (filter_files)
|
||||
*filter_files = atomic_load_explicit(&g_pxc_filter_files, memory_order_relaxed);
|
||||
if (filter_failed)
|
||||
*filter_failed = atomic_load_explicit(&g_pxc_filter_failed, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void cbm_pxc_dispatch_file(CBMLanguage lang, CBMFileResult *result, const char *source,
|
||||
int source_len, const char *rel, const char *def_module,
|
||||
const CBMCrossLspRegistries *cross_registries,
|
||||
@@ -1112,7 +1132,17 @@ void cbm_pxc_dispatch_file(CBMLanguage lang, CBMFileResult *result, const char *
|
||||
file_defs = filtered;
|
||||
file_def_count = filtered_count;
|
||||
}
|
||||
atomic_fetch_add_explicit(&g_pxc_filter_files, 1, memory_order_relaxed);
|
||||
if (!filter_succeeded) {
|
||||
atomic_fetch_add_explicit(&g_pxc_filter_failed, 1, memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
/* Per-file registry build cost is driven by THIS number. If it tracks the
|
||||
* corpus instead of the file's own module + imports, cross-file LSP is
|
||||
* O(files x corpus_defs) — see #1669. */
|
||||
atomic_fetch_add_explicit(&g_pxc_defs_registered, (uint64_t)file_def_count,
|
||||
memory_order_relaxed);
|
||||
atomic_fetch_add_explicit(&g_pxc_build_files, 1, memory_order_relaxed);
|
||||
if (lang == CBM_LANG_RUST) {
|
||||
CBMTypeRegistry *shared = rust_shared_get ? rust_shared_get(rust_shared_ctx) : NULL;
|
||||
if (shared) {
|
||||
|
||||
@@ -131,6 +131,11 @@ typedef struct {
|
||||
|
||||
/* Return the appropriate pre-built registry for a language, or NULL
|
||||
* if none was built (or language has no cross-LSP entrypoint). */
|
||||
/* Per-file registry-build cost (#1669): how many defs the per-file cross-LSP
|
||||
* path actually registered, and how often the module filter failed. */
|
||||
void cbm_pxc_filter_stats(uint64_t *defs_registered, uint64_t *build_files, uint64_t *filter_files,
|
||||
uint64_t *filter_failed);
|
||||
|
||||
static inline CBMTypeRegistry *cbm_pxc_registry_for_lang(const CBMCrossLspRegistries *r,
|
||||
CBMLanguage lang) {
|
||||
if (!r)
|
||||
|
||||
@@ -3315,6 +3315,26 @@ int cbm_parallel_resolve(cbm_pipeline_ctx_t *ctx, const cbm_file_info_t *files,
|
||||
cbm_log_info("parallel.resolve.cross_lsp_cost", "cross_lsp_ms", cu_buf, "files", nf_buf,
|
||||
"us_per_file", cf_buf, "defs_total", itoa_log(def_count),
|
||||
"us_per_file_per_kdef", pk_buf);
|
||||
|
||||
/* What the per-file registry build actually cost. defs_per_file is the
|
||||
* lever: if it tracks defs_total rather than the file's own module plus
|
||||
* imports, the module filter is not containing the work and cross-file
|
||||
* LSP is O(files x corpus_defs). */
|
||||
uint64_t reg_defs = 0;
|
||||
uint64_t reg_files = 0;
|
||||
uint64_t flt_files = 0;
|
||||
uint64_t flt_failed = 0;
|
||||
cbm_pxc_filter_stats(®_defs, ®_files, &flt_files, &flt_failed);
|
||||
if (reg_files > 0) {
|
||||
char rd_buf[CBM_SZ_32];
|
||||
char ff_buf[CBM_SZ_32];
|
||||
char fp_buf[CBM_SZ_32];
|
||||
snprintf(rd_buf, sizeof(rd_buf), "%llu", (unsigned long long)(reg_defs / reg_files));
|
||||
snprintf(ff_buf, sizeof(ff_buf), "%llu", (unsigned long long)flt_failed);
|
||||
snprintf(fp_buf, sizeof(fp_buf), "%llu", (unsigned long long)flt_files);
|
||||
cbm_log_info("parallel.resolve.perfile_registry", "defs_per_file", rd_buf, "defs_total",
|
||||
itoa_log(def_count), "filtered_files", fp_buf, "filter_failed", ff_buf);
|
||||
}
|
||||
}
|
||||
|
||||
cbm_log_info("parallel.resolve.done", "calls", itoa_log(total_calls), "usages",
|
||||
|
||||
Reference in New Issue
Block a user