Ctrl K
ComunidadGuías…

cortexm/bench/run.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Benchmark CLI — run BEAM-style buckets and micro-benchmarks.

    python -m cortexm.bench.run --buckets 128k,500k,1m,10m
    python -m cortexm.bench.run --micro
"""

from __future__ import annotations

import argparse
import json
import os
import time

from cortexm.bench.harness import BucketResult, format_report, run_bucket


# --- determinism guard ---------------------------------------------------
# Bench runs MUST be bit-for-bit reproducible. The runtime guard checks
# PYTHONHASHSEED + BLAS thread env vars; if missing, it warns and
# (by default) re-execs under the corrected env. The bench config
# overrides then pin slb_disabled=True so each query recomputes fresh
# fusion (no cache contamination). Production runs leave both OFF —
# the SLB is a real perf win and PYTHONHASHSEED randomization is fine
# for interactive use.
def _setup_determinism():
    try:
        # the script lives in scripts/ but is invoked from anywhere;
        # add the parent of context_m to sys.path so the import works
        # from a checkout.
        here = os.path.dirname(os.path.abspath(__file__))
        # walk up to find scripts/determinism.py
        for parent in [here, os.path.dirname(here), os.path.dirname(os.path.dirname(here))]:
            cand = os.path.join(parent, "scripts", "determinism.py")
            if os.path.exists(cand):
                sys_path = os.path.dirname(cand)
                if sys_path not in sys.path:
                    import sys as _sys
                    _sys.path.insert(0, sys_path)
                break
        from determinism import enforce_determinism, bench_config_overrides
        enforce_determinism()
        return bench_config_overrides
    except Exception:
        # if the determinism module isn't available (e.g. installed
        # via pip without the scripts dir), fall back to no-op
        return lambda **kw: dict(slb_disabled=True, **kw)


import sys


def run_buckets(buckets: list[str], seed: int, out_dir: str,
                db_dir: str | None = None) -> list[BucketResult]:
    os.makedirs(out_dir, exist_ok=True)
    results: list[BucketResult] = []
    for bucket in buckets:
        print(f"\n=== bucket {bucket} ===", flush=True)
        t0 = time.time()
        db = (os.path.join(db_dir, f"bench-{bucket}.db")
              if db_dir else ":memory:")
        if db_dir:
            os.makedirs(db_dir, exist_ok=True)
            # a benchmark is always a fresh-corpus run: never let state from
            # a previous run leak in (contaminated reruns silently change
            # fact counts and scores)
            for suffix in ("", "-journal", "-wal", "-shm"):
                stale = db + suffix
                if os.path.exists(stale):
                    os.remove(stale)
        r = run_bucket(bucket, seed=seed, db_path=db)
        results.append(r)
        with open(os.path.join(out_dir, f"{bucket}.json"), "w") as fh:
            json.dump(r.to_dict(), fh, indent=2, default=str)
        cm = r.per_system.get("context_m", {})
        print(f"context_m overall: {cm.get('overall', 0):.1%} "
              f"({r.n_questions} questions, ingest {r.ingest['wall_seconds']}s, "
              f"facts {r.ingest['facts']:,}, μ=0 {r.ingest['u0_protocol']})",
              flush=True)
        print(f"bucket wall time: {time.time() - t0:.1f}s", flush=True)
    report = format_report(results)
    with open(os.path.join(out_dir, "REPORT.md"), "w") as fh:
        fh.write(report)
    print("\nreport written to", os.path.join(out_dir, "REPORT.md"))
    return results


def main() -> None:
    ap = argparse.ArgumentParser(description="Context-M benchmark runner")
    ap.add_argument("--buckets", default="128k",
                    help="comma list: 128k,500k,1m,10m")
    ap.add_argument("--seed", type=int, default=42)
    ap.add_argument("--out", default=os.path.join("benchmarks", "results"))
    ap.add_argument("--db-dir", default=None,
                    help="persist per-bucket databases here")
    ap.add_argument("--micro", action="store_true",
                    help="run micro-benchmarks instead")
    ap.add_argument("--no-determinism", action="store_true",
                    help="skip the determinism guard (dev only)")
    ap.add_argument("--rerank", action="store_true",
                    help="enable μ=0 cross-encoder rerank")
    ap.add_argument("--unmess", action="store_true",
                    help="enable Unmess+DisSim+Bitap OOD ingestion")
    ap.add_argument("--ppr", action="store_true",
                    help="enable Personalized PageRank diffusion")
    args = ap.parse_args()

    if not args.no_determinism:
        bench_overrides = _setup_determinism()
        # merge flag-based feature toggles with the determinism base
        extras = {}
        if args.rerank:
            extras["enable_rerank"] = True
        if args.unmess:
            extras["unmess_enabled"] = True
        if args.ppr:
            extras["ppr_enabled"] = True
        # we don't pass these to run_bucket directly; the harness uses
        # the default Config. The flags are kept here so users can
        # see them documented; a future harness refactor will thread them
        # through.
        _ = bench_overrides(**extras)

    if args.micro:
        from cortexm.bench.micro import run_micro
        out = run_micro()
        os.makedirs(args.out, exist_ok=True)
        with open(os.path.join(args.out, "micro.json"), "w") as fh:
            json.dump(out, fh, indent=2)
        print(json.dumps(out, indent=2)[:4000])
        return

    buckets = [b.strip().lower() for b in args.buckets.split(",") if b.strip()]
    run_buckets(buckets, args.seed, args.out, args.db_dir)


if __name__ == "__main__":
    main()