cpu-perf 0.1.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. cpu_perf/__init__.py +15 -0
  2. cpu_perf/__main__.py +308 -0
  3. cpu_perf/brain.py +1034 -0
  4. cpu_perf/context.py +1019 -0
  5. cpu_perf/corpus/.github/ISSUE_TEMPLATE/add-entry.md +20 -0
  6. cpu_perf/corpus/.github/ISSUE_TEMPLATE/dead-or-moved-link.md +11 -0
  7. cpu_perf/corpus/.github/ISSUE_TEMPLATE/evidence-concern.md +11 -0
  8. cpu_perf/corpus/.github/pull_request_template.md +25 -0
  9. cpu_perf/corpus/CONTRIBUTING.md +119 -0
  10. cpu_perf/corpus/LICENSE +21 -0
  11. cpu_perf/corpus/README.md +727 -0
  12. cpu_perf/corpus/_build_info.json +259 -0
  13. cpu_perf/corpus/misc/README.md +18 -0
  14. cpu_perf/corpus/misc/benchmarks/02-branch-misprediction/README.md +130 -0
  15. cpu_perf/corpus/misc/benchmarks/02-branch-misprediction/bench.c +204 -0
  16. cpu_perf/corpus/misc/benchmarks/02-branch-misprediction/build.sh +12 -0
  17. cpu_perf/corpus/misc/benchmarks/02-branch-misprediction/results/raw.txt +86 -0
  18. cpu_perf/corpus/misc/benchmarks/02-branch-misprediction/results/summary.md +31 -0
  19. cpu_perf/corpus/misc/benchmarks/02-branch-misprediction/run.sh +101 -0
  20. cpu_perf/corpus/misc/benchmarks/03-latency-vs-throughput/README.md +208 -0
  21. cpu_perf/corpus/misc/benchmarks/03-latency-vs-throughput/bench.c +446 -0
  22. cpu_perf/corpus/misc/benchmarks/03-latency-vs-throughput/build.sh +14 -0
  23. cpu_perf/corpus/misc/benchmarks/03-latency-vs-throughput/results/raw.txt +141 -0
  24. cpu_perf/corpus/misc/benchmarks/03-latency-vs-throughput/results/summary.md +42 -0
  25. cpu_perf/corpus/misc/benchmarks/03-latency-vs-throughput/run.sh +255 -0
  26. cpu_perf/corpus/misc/benchmarks/04-cache-latency/README.md +116 -0
  27. cpu_perf/corpus/misc/benchmarks/04-cache-latency/bench.c +243 -0
  28. cpu_perf/corpus/misc/benchmarks/04-cache-latency/build.sh +12 -0
  29. cpu_perf/corpus/misc/benchmarks/04-cache-latency/results/raw.txt +307 -0
  30. cpu_perf/corpus/misc/benchmarks/04-cache-latency/results/summary.md +52 -0
  31. cpu_perf/corpus/misc/benchmarks/04-cache-latency/run.sh +145 -0
  32. cpu_perf/corpus/misc/benchmarks/05-measurement-pitfalls/README.md +135 -0
  33. cpu_perf/corpus/misc/benchmarks/05-measurement-pitfalls/bench.c +243 -0
  34. cpu_perf/corpus/misc/benchmarks/05-measurement-pitfalls/build.sh +14 -0
  35. cpu_perf/corpus/misc/benchmarks/05-measurement-pitfalls/results/raw.txt +505 -0
  36. cpu_perf/corpus/misc/benchmarks/05-measurement-pitfalls/results/summary.md +40 -0
  37. cpu_perf/corpus/misc/benchmarks/05-measurement-pitfalls/run.sh +252 -0
  38. cpu_perf/corpus/misc/benchmarks/06-roofline/README.md +246 -0
  39. cpu_perf/corpus/misc/benchmarks/06-roofline/bench.c +807 -0
  40. cpu_perf/corpus/misc/benchmarks/06-roofline/build.sh +12 -0
  41. cpu_perf/corpus/misc/benchmarks/06-roofline/results/raw.txt +477 -0
  42. cpu_perf/corpus/misc/benchmarks/06-roofline/results/summary.md +69 -0
  43. cpu_perf/corpus/misc/benchmarks/06-roofline/run.sh +126 -0
  44. cpu_perf/corpus/misc/benchmarks/07-aos-vs-soa-simd/README.md +286 -0
  45. cpu_perf/corpus/misc/benchmarks/07-aos-vs-soa-simd/bench.c +498 -0
  46. cpu_perf/corpus/misc/benchmarks/07-aos-vs-soa-simd/build.sh +42 -0
  47. cpu_perf/corpus/misc/benchmarks/07-aos-vs-soa-simd/results/raw.txt +162 -0
  48. cpu_perf/corpus/misc/benchmarks/07-aos-vs-soa-simd/results/summary.md +39 -0
  49. cpu_perf/corpus/misc/benchmarks/07-aos-vs-soa-simd/run.sh +123 -0
  50. cpu_perf/corpus/misc/benchmarks/08-autovectorization-aliasing/README.md +341 -0
  51. cpu_perf/corpus/misc/benchmarks/08-autovectorization-aliasing/bench.c +483 -0
  52. cpu_perf/corpus/misc/benchmarks/08-autovectorization-aliasing/build.sh +44 -0
  53. cpu_perf/corpus/misc/benchmarks/08-autovectorization-aliasing/results/raw.txt +509 -0
  54. cpu_perf/corpus/misc/benchmarks/08-autovectorization-aliasing/results/summary.md +64 -0
  55. cpu_perf/corpus/misc/benchmarks/08-autovectorization-aliasing/run.sh +189 -0
  56. cpu_perf/corpus/misc/benchmarks/09-false-sharing/README.md +149 -0
  57. cpu_perf/corpus/misc/benchmarks/09-false-sharing/bench.c +354 -0
  58. cpu_perf/corpus/misc/benchmarks/09-false-sharing/build.sh +12 -0
  59. cpu_perf/corpus/misc/benchmarks/09-false-sharing/results/raw.txt +257 -0
  60. cpu_perf/corpus/misc/benchmarks/09-false-sharing/results/summary.md +50 -0
  61. cpu_perf/corpus/misc/benchmarks/09-false-sharing/run.sh +144 -0
  62. cpu_perf/corpus/misc/benchmarks/10-first-touch/README.md +161 -0
  63. cpu_perf/corpus/misc/benchmarks/10-first-touch/bench.c +482 -0
  64. cpu_perf/corpus/misc/benchmarks/10-first-touch/build.sh +12 -0
  65. cpu_perf/corpus/misc/benchmarks/10-first-touch/results/raw.txt +168 -0
  66. cpu_perf/corpus/misc/benchmarks/10-first-touch/results/summary.md +52 -0
  67. cpu_perf/corpus/misc/benchmarks/10-first-touch/run.sh +146 -0
  68. cpu_perf/corpus/misc/benchmarks/11-syscall-cost/README.md +151 -0
  69. cpu_perf/corpus/misc/benchmarks/11-syscall-cost/bench.c +315 -0
  70. cpu_perf/corpus/misc/benchmarks/11-syscall-cost/build.sh +12 -0
  71. cpu_perf/corpus/misc/benchmarks/11-syscall-cost/results/raw.txt +134 -0
  72. cpu_perf/corpus/misc/benchmarks/11-syscall-cost/results/summary.md +42 -0
  73. cpu_perf/corpus/misc/benchmarks/11-syscall-cost/run.sh +137 -0
  74. cpu_perf/corpus/misc/benchmarks/12-coordinated-omission/README.md +179 -0
  75. cpu_perf/corpus/misc/benchmarks/12-coordinated-omission/bench.c +460 -0
  76. cpu_perf/corpus/misc/benchmarks/12-coordinated-omission/build.sh +12 -0
  77. cpu_perf/corpus/misc/benchmarks/12-coordinated-omission/results/raw.txt +150 -0
  78. cpu_perf/corpus/misc/benchmarks/12-coordinated-omission/results/summary.md +34 -0
  79. cpu_perf/corpus/misc/benchmarks/12-coordinated-omission/run.sh +105 -0
  80. cpu_perf/corpus/misc/benchmarks/13-sgemm-naive-vs-blas/README.md +169 -0
  81. cpu_perf/corpus/misc/benchmarks/13-sgemm-naive-vs-blas/bench.c +636 -0
  82. cpu_perf/corpus/misc/benchmarks/13-sgemm-naive-vs-blas/build.sh +23 -0
  83. cpu_perf/corpus/misc/benchmarks/13-sgemm-naive-vs-blas/results/raw.txt +102 -0
  84. cpu_perf/corpus/misc/benchmarks/13-sgemm-naive-vs-blas/results/summary.md +51 -0
  85. cpu_perf/corpus/misc/benchmarks/13-sgemm-naive-vs-blas/run.sh +158 -0
  86. cpu_perf/corpus/misc/benchmarks/14-pcore-vs-ecore/README.md +150 -0
  87. cpu_perf/corpus/misc/benchmarks/14-pcore-vs-ecore/bench.c +612 -0
  88. cpu_perf/corpus/misc/benchmarks/14-pcore-vs-ecore/build.sh +13 -0
  89. cpu_perf/corpus/misc/benchmarks/14-pcore-vs-ecore/results/raw.txt +194 -0
  90. cpu_perf/corpus/misc/benchmarks/14-pcore-vs-ecore/results/summary.md +35 -0
  91. cpu_perf/corpus/misc/benchmarks/14-pcore-vs-ecore/run.sh +187 -0
  92. cpu_perf/corpus/misc/benchmarks/15-stream-bandwidth/README.md +137 -0
  93. cpu_perf/corpus/misc/benchmarks/15-stream-bandwidth/bench.c +426 -0
  94. cpu_perf/corpus/misc/benchmarks/15-stream-bandwidth/build.sh +12 -0
  95. cpu_perf/corpus/misc/benchmarks/15-stream-bandwidth/results/raw.txt +189 -0
  96. cpu_perf/corpus/misc/benchmarks/15-stream-bandwidth/results/summary.md +29 -0
  97. cpu_perf/corpus/misc/benchmarks/15-stream-bandwidth/run.sh +140 -0
  98. cpu_perf/corpus/misc/benchmarks/README.md +72 -0
  99. cpu_perf/corpus/misc/benchmarks/common/clock_estimate.c +46 -0
  100. cpu_perf/corpus/misc/benchmarks/common/machine.sh +30 -0
  101. cpu_perf/corpus/misc/benchmarks/common/refresh_readme.py +28 -0
  102. cpu_perf/corpus/misc/benchmarks/common/timing.h +75 -0
  103. cpu_perf/corpus/misc/benchmarks/compile_all.sh +18 -0
  104. cpu_perf/corpus/misc/benchmarks/run_all.sh +19 -0
  105. cpu_perf/corpus/misc/notes/banner/README.md +63 -0
  106. cpu_perf/corpus/misc/notes/benchmark-brief.md +97 -0
  107. cpu_perf/corpus/misc/notes/changelog.md +881 -0
  108. cpu_perf/corpus/misc/notes/conventions.md +116 -0
  109. cpu_perf/corpus/misc/notes/owner-brief.md +102 -0
  110. cpu_perf/corpus/misc/notes/sections/01-start-here.md +78 -0
  111. cpu_perf/corpus/misc/notes/sections/02-one-instruction.md +99 -0
  112. cpu_perf/corpus/misc/notes/sections/03-microarchitecture.md +114 -0
  113. cpu_perf/corpus/misc/notes/sections/04-memory-hierarchy.md +101 -0
  114. cpu_perf/corpus/misc/notes/sections/05-measurement.md +95 -0
  115. cpu_perf/corpus/misc/notes/sections/06-models.md +119 -0
  116. cpu_perf/corpus/misc/notes/sections/07-single-thread.md +118 -0
  117. cpu_perf/corpus/misc/notes/sections/08-compilers.md +113 -0
  118. cpu_perf/corpus/misc/notes/sections/09-concurrency.md +140 -0
  119. cpu_perf/corpus/misc/notes/sections/10-numa.md +104 -0
  120. cpu_perf/corpus/misc/notes/sections/11-os-and-io.md +127 -0
  121. cpu_perf/corpus/misc/notes/sections/12-tail-latency.md +129 -0
  122. cpu_perf/corpus/misc/notes/sections/13-inference-on-cpu.md +145 -0
  123. cpu_perf/corpus/misc/notes/sections/14-hardware-generations.md +140 -0
  124. cpu_perf/corpus/misc/notes/sections/15-benchmarks.md +109 -0
  125. cpu_perf/corpus/misc/notes/sections/16-watchlist.md +132 -0
  126. cpu_perf/corpus/misc/notes/status.md +183 -0
  127. cpu_perf/corpus/misc/notes/voice.md +52 -0
  128. cpu_perf/corpus/misc/scripts/build_changelog.py +86 -0
  129. cpu_perf/corpus/misc/scripts/check_format.py +256 -0
  130. cpu_perf/corpus/misc/scripts/check_links.py +287 -0
  131. cpu_perf/corpus.py +344 -0
  132. cpu_perf/corpus_update.py +286 -0
  133. cpu_perf/evidence.py +163 -0
  134. cpu_perf/grammar.py +70 -0
  135. cpu_perf/library/__init__.py +2 -0
  136. cpu_perf/library/chunk.py +85 -0
  137. cpu_perf/library/crawler.py +445 -0
  138. cpu_perf/library/embed.py +104 -0
  139. cpu_perf/library/extract.py +502 -0
  140. cpu_perf/library/resolve.py +172 -0
  141. cpu_perf/library/retrieve.py +359 -0
  142. cpu_perf/library/service.py +441 -0
  143. cpu_perf/library/store.py +576 -0
  144. cpu_perf/locate.py +180 -0
  145. cpu_perf/manifest.py +180 -0
  146. cpu_perf/models.py +311 -0
  147. cpu_perf/net.py +405 -0
  148. cpu_perf/parse_benchmarks.py +165 -0
  149. cpu_perf/parse_readme.py +237 -0
  150. cpu_perf/parse_record.py +257 -0
  151. cpu_perf/prompts.py +124 -0
  152. cpu_perf/render.py +325 -0
  153. cpu_perf/resolve.py +166 -0
  154. cpu_perf/schema.py +162 -0
  155. cpu_perf/search.py +367 -0
  156. cpu_perf/server.py +465 -0
  157. cpu_perf/snippets.py +76 -0
  158. cpu_perf/text.py +260 -0
  159. cpu_perf-0.1.0rc1.dist-info/METADATA +400 -0
  160. cpu_perf-0.1.0rc1.dist-info/RECORD +162 -0
  161. cpu_perf-0.1.0rc1.dist-info/WHEEL +4 -0
  162. cpu_perf-0.1.0rc1.dist-info/entry_points.txt +2 -0
cpu_perf/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ """cpu-perf: the CPU Performance Engineering list as an MCP server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ try:
6
+ from importlib.metadata import PackageNotFoundError, version
7
+
8
+ try:
9
+ __version__ = version("cpu-perf")
10
+ except PackageNotFoundError: # running from a checkout without an install
11
+ __version__ = "0+checkout"
12
+ except ImportError: # pragma: no cover
13
+ __version__ = "0+checkout"
14
+
15
+ REPO_URL = "https://github.com/usamahz/cpu-performance-engineering"
cpu_perf/__main__.py ADDED
@@ -0,0 +1,308 @@
1
+ """Command line: serve (stdio or HTTP), build the library, show status, self-test."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import logging
8
+ import os
9
+ import statistics
10
+ import sys
11
+ import threading
12
+ import time
13
+
14
+ from . import __version__
15
+
16
+
17
+ def _env_bool(name: str, default: bool) -> bool:
18
+ val = os.environ.get(name)
19
+ if val is None:
20
+ return default
21
+ return val.strip().lower() not in ("0", "false", "no", "off", "")
22
+
23
+
24
+ def _common(p: argparse.ArgumentParser) -> None:
25
+ p.add_argument("--repo", default=os.environ.get("CPU_PERF_REPO"), help="serve a checkout of the repository instead of the bundled copy")
26
+ p.add_argument("--data-dir", default=os.environ.get("CPU_PERF_DATA_DIR"), help="where the source library lives")
27
+ p.add_argument("--embed-model", default=os.environ.get("CPU_PERF_EMBED_MODEL"), help="model2vec model id, 'hashing', or 'none' for keyword search only")
28
+ p.add_argument("--no-library", action="store_true", default=not _env_bool("CPU_PERF_LIBRARY", True), help="repository knowledge only: no source library")
29
+ p.add_argument("--ignore-robots", action="store_true", default=_env_bool("CPU_PERF_IGNORE_ROBOTS", False), help="crawl even where robots.txt disallows")
30
+ p.add_argument("--no-auto-update", action="store_true", default=not _env_bool("CPU_PERF_AUTO_UPDATE", True), help="serve the installed copy of the list; no daily check for a newer one")
31
+ p.add_argument("--log-level", default=os.environ.get("CPU_PERF_LOG_LEVEL", "WARNING"))
32
+
33
+
34
+ def build_parser() -> argparse.ArgumentParser:
35
+ parser = argparse.ArgumentParser(prog="cpu-perf", description="MCP server for the CPU Performance Engineering list.")
36
+ parser.add_argument("--version", action="version", version=f"cpu-perf {__version__}")
37
+ parser.add_argument("--selftest", action="store_true", help="load the corpus, check it, time it, and exit")
38
+ sub = parser.add_subparsers(dest="command")
39
+
40
+ serve = sub.add_parser("serve", help="run the MCP server (default)")
41
+ _common(serve)
42
+ serve.add_argument("--transport", choices=("stdio", "http"), default=os.environ.get("CPU_PERF_TRANSPORT", "stdio"))
43
+ serve.add_argument("--host", default=os.environ.get("CPU_PERF_HOST", "127.0.0.1"))
44
+ serve.add_argument("--port", type=int, default=int(os.environ.get("CPU_PERF_PORT") or os.environ.get("PORT") or 8000))
45
+ serve.add_argument("--path", default=os.environ.get("CPU_PERF_PATH", "/mcp"))
46
+ serve.add_argument("--no-auto-index", action="store_true", default=not _env_bool("CPU_PERF_AUTO_INDEX", True), help="do not build the library in the background")
47
+ serve.add_argument("--no-live-fetch", action="store_true", default=not _env_bool("CPU_PERF_LIVE_FETCH", True), help="read_source serves only what is already indexed")
48
+ serve.add_argument("--structured-output", action="store_true", default=_env_bool("CPU_PERF_STRUCTURED_OUTPUT", False), help="also return structured content and output schemas (for programmatic clients; chat clients read the markdown)")
49
+ serve.add_argument("--allowed-host", action="append", default=[h for h in os.environ.get("CPU_PERF_ALLOWED_HOSTS", "").split(",") if h], help="Host header values to accept over HTTP (DNS-rebinding protection)")
50
+
51
+ index = sub.add_parser("index", help="build or refresh the source library now")
52
+ _common(index)
53
+ index.add_argument("--refresh", action="store_true", help="re-check every source, not only missing or stale ones")
54
+ index.add_argument("--only", action="append", default=[], help="a URL or entry id to (re)fetch; repeatable")
55
+ index.add_argument("--max-pages", type=int, default=2500, help="pages indexed per PDF")
56
+ index.add_argument("--workers", type=int, default=6)
57
+ index.add_argument("--no-embeddings", action="store_true", help="keyword index only")
58
+
59
+ status = sub.add_parser("status", help="show the library's coverage")
60
+ _common(status)
61
+ status.add_argument("--detail", action="store_true")
62
+ status.add_argument("--json", action="store_true")
63
+ return parser
64
+
65
+
66
+ def _setup_logging(level: str) -> None:
67
+ logging.basicConfig(stream=sys.stderr, level=getattr(logging, str(level).upper(), logging.WARNING), format="cpu-perf %(levelname)s %(message)s")
68
+
69
+
70
+ def _data_dir(args):
71
+ from pathlib import Path
72
+
73
+ from .library.store import default_data_dir
74
+
75
+ return Path(args.data_dir).expanduser() if args.data_dir else default_data_dir()
76
+
77
+
78
+ def _brain(args, *, auto_index: bool, live_fetch: bool, library: bool = True, workers: int = 6, max_pages: int = 2500):
79
+ from .brain import Brain
80
+ from .corpus import load
81
+ from .library.service import LibraryService
82
+ from .locate import locate
83
+ from .search import SearchIndex
84
+
85
+ # with the daily update on, a newer list it downloaded wins over the installed copy
86
+ reader = locate(args.repo, data_dir=None if args.no_auto_update else _data_dir(args))
87
+ reader.preload()
88
+ corpus = load(reader)
89
+ index = SearchIndex(corpus)
90
+ lib = None
91
+ if library and not args.no_library:
92
+ lib = LibraryService(
93
+ corpus,
94
+ index.expand,
95
+ data_dir=args.data_dir,
96
+ embed_model=args.embed_model,
97
+ auto_index=auto_index,
98
+ live_fetch=live_fetch,
99
+ respect_robots=not args.ignore_robots,
100
+ workers=workers,
101
+ max_pages=max_pages,
102
+ )
103
+ return Brain(corpus, index, lib)
104
+
105
+
106
+ def validate_corpus(reader, current):
107
+ """A downloaded list must parse and must not have collapsed against the one served."""
108
+ from .corpus import load
109
+
110
+ reader.preload()
111
+ corpus = load(reader)
112
+ new, old = corpus.stats(), current.stats()
113
+ for key in ("sections", "linked_entries", "benchmarks"):
114
+ if new[key] < 0.8 * old[key]:
115
+ raise ValueError(f"{key} fell from {old[key]} to {new[key]}")
116
+ return corpus
117
+
118
+
119
+ def start_daily_update(holder, args, updater=None):
120
+ """Check once a day for a newer list and swap it in; returns the updater or None."""
121
+ from .brain import Brain
122
+ from .corpus_update import CorpusUpdater
123
+ from .search import SearchIndex
124
+
125
+ brain = holder.current
126
+ if args.no_auto_update or brain.c.reader.source not in ("bundled", "downloaded"):
127
+ return None
128
+ updater = updater or CorpusUpdater(_data_dir(args))
129
+ brain.updater = updater
130
+ log = logging.getLogger("cpu_perf.update")
131
+
132
+ def tick():
133
+ cur = holder.current
134
+ built = {}
135
+
136
+ def check(reader):
137
+ built["corpus"] = validate_corpus(reader, cur.c)
138
+
139
+ newer = updater.run(cur.c.reader, validate=check)
140
+ if newer is None:
141
+ return
142
+ corpus = built.get("corpus") or validate_corpus(newer, cur.c)
143
+ holder.swap(Brain(corpus, SearchIndex(corpus), cur.lib))
144
+ log.warning("now serving the list at commit %s", (newer.commit or "")[:12])
145
+
146
+ if brain.lib is not None:
147
+ brain.lib.on_tick = tick
148
+ else:
149
+ def loop():
150
+ while True:
151
+ try:
152
+ tick()
153
+ except Exception:
154
+ log.exception("list update failed")
155
+ time.sleep(float(os.environ.get("CPU_PERF_MAINTENANCE_SECONDS", "300")))
156
+
157
+ threading.Thread(target=loop, name="list-update", daemon=True).start()
158
+ return updater
159
+
160
+
161
+ def cmd_serve(args) -> int:
162
+ from .brain import BrainHolder
163
+ from .server import create_server
164
+
165
+ brain = _brain(args, auto_index=not args.no_auto_index, live_fetch=not args.no_live_fetch)
166
+ holder = BrainHolder(brain)
167
+ srv = create_server(holder, live_fetch=not args.no_live_fetch, structured=args.structured_output)
168
+ start_daily_update(holder, args)
169
+ if brain.lib is not None:
170
+ brain.lib.start()
171
+ if args.transport == "stdio":
172
+ srv.run("stdio")
173
+ return 0
174
+ from mcp.server.transport_security import TransportSecuritySettings
175
+
176
+ security = None
177
+ if args.allowed_host:
178
+ hosts = list(args.allowed_host)
179
+ security = TransportSecuritySettings(
180
+ enable_dns_rebinding_protection=True,
181
+ allowed_hosts=hosts + [f"{h}:*" for h in hosts if ":" not in h],
182
+ allowed_origins=[f"https://{h}" for h in hosts] + [f"http://{h}" for h in hosts],
183
+ )
184
+ elif args.host not in ("127.0.0.1", "localhost", "::1"):
185
+ logging.getLogger("cpu_perf").warning("serving on %s without --allowed-host: DNS-rebinding protection is off", args.host)
186
+ security = TransportSecuritySettings(enable_dns_rebinding_protection=False)
187
+ srv.run(
188
+ "streamable-http",
189
+ host=args.host,
190
+ port=args.port,
191
+ streamable_http_path=args.path,
192
+ json_response=True,
193
+ stateless_http=True,
194
+ transport_security=security,
195
+ )
196
+ return 0
197
+
198
+
199
+ def cmd_index(args) -> int:
200
+ brain = _brain(args, auto_index=False, live_fetch=False, workers=args.workers, max_pages=args.max_pages)
201
+ lib = brain.lib
202
+ if lib is None:
203
+ print("the library is disabled (--no-library)", file=sys.stderr)
204
+ return 2
205
+ if args.no_embeddings:
206
+ lib.embed_model = "none"
207
+ print(f"library: {lib.data_dir}", file=sys.stderr)
208
+ print("loading the embedding model...", file=sys.stderr)
209
+ lib.load_embedder()
210
+ print(f"embedder: {lib.embedder_state}", file=sys.stderr)
211
+ if not lib.lock.acquire():
212
+ print("another process is building the library; try again later", file=sys.stderr)
213
+ return 1
214
+ stop = threading.Event()
215
+
216
+ def report():
217
+ while not stop.wait(3):
218
+ p = lib.crawler.progress
219
+ cur = ", ".join(u.split("/")[2] for u in p.current[:3])
220
+ print(f" {p.done}/{p.total} sources {dict(p.counts)} now: {cur}", file=sys.stderr)
221
+
222
+ t = threading.Thread(target=report, daemon=True)
223
+ t.start()
224
+ try:
225
+ if lib.store.stale_normalisation():
226
+ print(f"re-normalised {lib.renormalise()} passages for the current tokenizer", file=sys.stderr)
227
+ summary = lib.crawler.run(only=args.only or None, refresh=args.refresh)
228
+ finally:
229
+ stop.set()
230
+ lib.lock.release()
231
+ print(f"done: {summary['done']}/{summary['total']} {summary['counts']}", file=sys.stderr)
232
+ print(lib.coverage_line())
233
+ return 0
234
+
235
+
236
+ def cmd_status(args) -> int:
237
+ from . import render
238
+
239
+ brain = _brain(args, auto_index=False, live_fetch=False)
240
+ out = brain.library_status(args.detail)
241
+ if args.json:
242
+ print(json.dumps(out.model_dump(mode="json"), indent=1))
243
+ else:
244
+ print(render.status(out))
245
+ return 0
246
+
247
+
248
+ def cmd_selftest(args) -> int:
249
+ from .corpus import load
250
+ from .locate import locate
251
+ from .search import SearchIndex
252
+
253
+ t0 = time.perf_counter()
254
+ corpus = load(locate(getattr(args, "repo", None) or os.environ.get("CPU_PERF_REPO")))
255
+ t1 = time.perf_counter()
256
+ index = SearchIndex(corpus)
257
+ t2 = time.perf_counter()
258
+ stats = corpus.stats()
259
+ problems = list(corpus.warnings)
260
+ if corpus.badge_entries is not None and corpus.badge_entries != stats["linked_entries"]:
261
+ problems.append("entry count differs from the README badge")
262
+ if corpus.badge_benchmarks is not None and corpus.badge_benchmarks != stats["benchmarks"]:
263
+ problems.append("benchmark count differs from the README badge")
264
+ queries = ["false sharing", "branch misprediction penalty", "numa first touch", "roofline", "tail latency p99",
265
+ "auto-vectorisation aliasing", "int8 quantization", "store forwarding", "huge pages tlb", "top-down"]
266
+ times = []
267
+ for _ in range(20):
268
+ for q in queries:
269
+ s = time.perf_counter()
270
+ index.search(q, limit=10)
271
+ times.append((time.perf_counter() - s) * 1000)
272
+ times.sort()
273
+ print(f"cpu-perf {__version__}")
274
+ print(f"source: {corpus.reader.source} ({corpus.reader.describe()})")
275
+ for k, v in stats.items():
276
+ print(f"{k}: {v}")
277
+ print(f"load_ms: {(t1 - t0) * 1000:.1f}")
278
+ print(f"index_ms: {(t2 - t1) * 1000:.1f}")
279
+ print(f"query_ms p50: {statistics.median(times):.3f} p99: {times[int(len(times) * 0.99) - 1]:.3f}")
280
+ for p in problems:
281
+ print(f"PROBLEM: {p}")
282
+ return 1 if problems else 0
283
+
284
+
285
+ def main(argv: list[str] | None = None) -> int:
286
+ argv = list(sys.argv[1:] if argv is None else argv)
287
+ parser = build_parser()
288
+ commands = {"serve", "index", "status"}
289
+ if "--selftest" in argv:
290
+ args = parser.parse_args(["--selftest"])
291
+ rest = [a for a in argv if a != "--selftest"]
292
+ args.repo = None
293
+ if "--repo" in rest:
294
+ args.repo = rest[rest.index("--repo") + 1]
295
+ return cmd_selftest(args)
296
+ if not argv or argv[0] not in commands and not argv[0] in ("-h", "--help", "--version"):
297
+ argv = ["serve", *argv]
298
+ args = parser.parse_args(argv)
299
+ _setup_logging(getattr(args, "log_level", "WARNING"))
300
+ if args.command == "index":
301
+ return cmd_index(args)
302
+ if args.command == "status":
303
+ return cmd_status(args)
304
+ return cmd_serve(args)
305
+
306
+
307
+ if __name__ == "__main__":
308
+ sys.exit(main())