Measure genuine CPU query startup first input and warm latency
This commit is contained in:
1 parent
6e7616b6d3
commit
d568bdb2b1
2 files changed
+43
-1
No files matched your search
@@ -93,3 +93,32 @@ and six paired measurements also passed; see
|
|||||||
effectively unchanged in those samples. Native macOS and actual GPU context
|
effectively unchanged in those samples. Native macOS and actual GPU context
|
||||||
allocation/parity, pressure behavior, long code queries, representative query
|
allocation/parity, pressure behavior, long code queries, representative query
|
||||||
latency and updated installed desktop packaging remain release checks.
|
latency and updated installed desktop packaging remain release checks.
|
||||||
|
|
||||||
|
## Current Windows CPU worker timing, 2026-10-13
|
||||||
|
|
||||||
|
The normal current worker (`c01cb3ec13cfca722ee768e8350e1399a8b9a4d14a24a35adc3473a5cfc82a73`)
|
||||||
|
was measured with CPU explicitly selected and one inference thread. Three paired
|
||||||
|
indexing/query processes alternate order; each retains a separate first request
|
||||||
|
and twenty distinct warmed inputs with native debug timings. Real 768-dimensional
|
||||||
|
vectors were checked. Output preservation now refuses existing reports.
|
||||||
|
|
||||||
|
| Query-role repeat | Process startup ms | First inference/IPC ms | Warm p95 ms (20 samples) |
|
||||||
|
| --- | ---: | ---: | ---: |
|
||||||
|
| 0 | 729.27 | 100.02 | 71.30 |
|
||||||
|
| 1 | 662.83 | 106.18 | 78.38 |
|
||||||
|
| 2 | 704.21 | 100.84 | 71.75 |
|
||||||
|
|
||||||
|
One-time text-model size/SHA verification took 192.45 ms and is separate from
|
||||||
|
worker startup. Indexing-role startup ranged 624.49–779.14 ms, first query
|
||||||
|
101.64–110.53 ms, warm p95 73.03–80.47 ms. These are cached-file native-worker
|
||||||
|
measurements, not cold disk, Rust query-cache, ANN, hybrid retrieval, actual UI
|
||||||
|
first input or paint. The installed desktop/query worker remained present;
|
||||||
|
background activity was not controlled, so this is not an idle reference-host
|
||||||
|
acceptance measurement. All test inference used CPU; no GPU provider was loaded.
|
||||||
|
|
||||||
|
Full ignored report: `.searchi/qa/query-context-warm-current.json`, SHA-256
|
||||||
|
`ea7dd0b72eeb1dbd79f06fce92e0475dfdc321d7781e5d06d58fe12755faf0fd`.
|
||||||
|
The query-role memory observation after first inference was 239,878,144 bytes
|
||||||
|
private commitment and 274,141,184 bytes resident working set for repeat zero;
|
||||||
|
these are different metrics and must not be summed. Native desktop first-input
|
||||||
|
and complete search latency remain separate open checks.
|
||||||
@@ -8,6 +8,7 @@ import argparse
|
|||||||
import ctypes
|
import ctypes
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import platform
|
import platform
|
||||||
@@ -53,6 +54,8 @@ def main():
|
|||||||
parser.add_argument("--output", type=Path, required=True)
|
parser.add_argument("--output", type=Path, required=True)
|
||||||
parser.add_argument("--repeats", type=int, default=3)
|
parser.add_argument("--repeats", type=int, default=3)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
if args.output.exists():
|
||||||
|
parser.error("output must be new; preserve previous evidence")
|
||||||
if not 1 <= args.repeats <= 20:
|
if not 1 <= args.repeats <= 20:
|
||||||
parser.error("repeats must be 1..20")
|
parser.error("repeats must be 1..20")
|
||||||
manifest = json.loads((ROOT / "tools/embeddinggemma2/model-manifest.json").read_text())
|
manifest = json.loads((ROOT / "tools/embeddinggemma2/model-manifest.json").read_text())
|
||||||
@@ -77,6 +80,15 @@ def main():
|
|||||||
response, first_ms = session.worker({"op": "query", "text": "cancel the supplier partnership", "debug_performance": True})
|
response, first_ms = session.worker({"op": "query", "text": "cancel the supplier partnership", "debug_performance": True})
|
||||||
vectors(response, 1)
|
vectors(response, 1)
|
||||||
sample.update(first_query_ms=first_ms, worker=response["performance"], after_query_memory=memory(session.process.pid))
|
sample.update(first_query_ms=first_ms, worker=response["performance"], after_query_memory=memory(session.process.pid))
|
||||||
|
warm = []
|
||||||
|
# Distinct inputs exercise native query inference without a Rust
|
||||||
|
# query cache. Keep first input separate from these warmed calls.
|
||||||
|
for index in range(20):
|
||||||
|
response, duration = session.worker({"op": "query", "text": f"supplier cancellation notice example {index}", "debug_performance": True})
|
||||||
|
vectors(response, 1)
|
||||||
|
warm.append({"round_trip_ms": duration, "worker": response["performance"]})
|
||||||
|
ordered = sorted(item["round_trip_ms"] for item in warm)
|
||||||
|
sample["warm_queries"] = {"samples": warm, "p95_ms": ordered[math.ceil(len(ordered) * .95) - 1]}
|
||||||
if role == "query":
|
if role == "query":
|
||||||
session.send({"op": "documents", "texts": ["test"], "title": "none"})
|
session.send({"op": "documents", "texts": ["test"], "title": "none"})
|
||||||
rejected = session.next()
|
rejected = session.next()
|
||||||
@@ -88,7 +100,8 @@ def main():
|
|||||||
session.close()
|
session.close()
|
||||||
report = {"platform": platform.platform(), "cpu_count": os.cpu_count(), "verification_ms": verification_ms, "cache_note": "Cached model bytes; startup excludes Rust verification and desktop preparation. No GPU executed. Repeated fresh processes, not cold disk.", "worker_sha256": hashlib.sha256(args.worker.read_bytes()).hexdigest(), "samples": samples}
|
report = {"platform": platform.platform(), "cpu_count": os.cpu_count(), "verification_ms": verification_ms, "cache_note": "Cached model bytes; startup excludes Rust verification and desktop preparation. No GPU executed. Repeated fresh processes, not cold disk.", "worker_sha256": hashlib.sha256(args.worker.read_bytes()).hexdigest(), "samples": samples}
|
||||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
args.output.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
with args.output.open("x", encoding="utf-8") as output:
|
||||||
|
output.write(json.dumps(report, indent=2) + "\n")
|
||||||
for role in ["indexing", "query"]:
|
for role in ["indexing", "query"]:
|
||||||
rows = [s for s in samples if s["role"] == role]
|
rows = [s for s in samples if s["role"] == role]
|
||||||
print(role, "startup median ms", statistics.median(s["startup_ms"] for s in rows), "ready memory", rows[0]["ready_memory"], "after query", rows[0]["after_query_memory"])
|
print(role, "startup median ms", statistics.median(s["startup_ms"] for s in rows), "ready memory", rows[0]["ready_memory"], "after query", rows[0]["after_query_memory"])
|
||||||
|
|||||||
Reference in new issue
Block a user