Keep real model benchmark validity checks active under optimization
This commit is contained in:
1 parent
d568bdb2b1
commit
66bf7d7b47
4 files changed
+79
-27
No files matched your search
@@ -122,3 +122,16 @@ 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;
|
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
|
these are different metrics and must not be summed. Native desktop first-input
|
||||||
and complete search latency remain separate open checks.
|
and complete search latency remain separate open checks.
|
||||||
|
|
||||||
|
Benchmark contract hardening then replaced Python assertions with explicit
|
||||||
|
errors in both shared benchmark and query-context tooling. Model hashes,
|
||||||
|
CPU/device state, role/context, normalized vectors, source-window preservation,
|
||||||
|
MCP errors/filter behavior and batch parity remain checked under `python -O`.
|
||||||
|
Both tools refuse existing output reports. Four targeted invalid-vector/device
|
||||||
|
tests passed in normal and optimized modes. A genuine one-pair Google CPU
|
||||||
|
query/indexing run also passed under optimization, retaining twenty warmed
|
||||||
|
requests per role; this is a contract smoke rather than an additional latency
|
||||||
|
acceptance sample. Ignored report SHA-256:
|
||||||
|
`e611fd3c705a94a6c06ab6ae6a934e6f604665aacb17d0e86d6d5b162ac59e46`
|
||||||
|
(`.searchi/qa/query-context-optimized-contract.json`). Earlier recorded timing
|
||||||
|
reports were run without optimization and are unaffected by this correction.
|
||||||
@@ -38,6 +38,11 @@ def distribution(values):
|
|||||||
'p50_ms': statistics.median(values),
|
'p50_ms': statistics.median(values),
|
||||||
'p95_ms': ordered[max(0, math.ceil(len(values) * .95) - 1)]}
|
'p95_ms': ordered[max(0, math.ceil(len(values) * .95) - 1)]}
|
||||||
|
|
||||||
|
def require(condition, message):
|
||||||
|
if not condition:
|
||||||
|
raise ValueError(message)
|
||||||
|
|
||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
def __init__(self, command):
|
def __init__(self, command):
|
||||||
environment = os.environ.copy()
|
environment = os.environ.copy()
|
||||||
@@ -81,7 +86,7 @@ class Session:
|
|||||||
response = self.next()
|
response = self.next()
|
||||||
if not response.get('ok'):
|
if not response.get('ok'):
|
||||||
raise RuntimeError(response.get('error', 'Worker request failed'))
|
raise RuntimeError(response.get('error', 'Worker request failed'))
|
||||||
assert response['device'] == 'cpu'
|
require(response['device'] == 'cpu', 'Benchmark contract failed at benchmark.py:84')
|
||||||
return response, elapsed(start)
|
return response, elapsed(start)
|
||||||
|
|
||||||
def mcp(self, identifier, method, params):
|
def mcp(self, identifier, method, params):
|
||||||
@@ -90,7 +95,7 @@ class Session:
|
|||||||
while True:
|
while True:
|
||||||
response = self.next()
|
response = self.next()
|
||||||
if response.get('id') == identifier:
|
if response.get('id') == identifier:
|
||||||
assert 'error' not in response, response
|
require('error' not in response, response)
|
||||||
return response['result'], elapsed(start)
|
return response['result'], elapsed(start)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@@ -103,19 +108,19 @@ class Session:
|
|||||||
|
|
||||||
def vectors(response, count):
|
def vectors(response, count):
|
||||||
result = response['vectors']
|
result = response['vectors']
|
||||||
assert len(result) == count
|
require(len(result) == count, 'Benchmark contract failed at benchmark.py:106')
|
||||||
for vector in result:
|
for vector in result:
|
||||||
assert len(vector) == 768 and all(math.isfinite(n) for n in vector)
|
require(len(vector) == 768 and all(math.isfinite(n) for n in vector), 'Benchmark contract failed at benchmark.py:108')
|
||||||
assert abs(sum(n*n for n in vector) - 1) < .0001
|
require(abs(sum(n*n for n in vector) - 1) < .0001, 'Benchmark contract failed at benchmark.py:109')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def native(args):
|
def native(args):
|
||||||
manifest = json.loads((ROOT / 'tools/embeddinggemma2/model-manifest.json').read_text())
|
manifest = json.loads((ROOT / 'tools/embeddinggemma2/model-manifest.json').read_text())
|
||||||
model = args.models / manifest['files'][0]['path']
|
model = args.models / manifest['files'][0]['path']
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
assert model.stat().st_size == manifest['files'][0]['bytes']
|
require(model.stat().st_size == manifest['files'][0]['bytes'], 'Benchmark contract failed at benchmark.py:116')
|
||||||
with model.open('rb') as stream:
|
with model.open('rb') as stream:
|
||||||
assert hashlib.file_digest(stream, 'sha256').hexdigest() == manifest['files'][0]['sha256']
|
require(hashlib.file_digest(stream, 'sha256').hexdigest() == manifest['files'][0]['sha256'], 'Benchmark contract failed at benchmark.py:118')
|
||||||
verification_ms = elapsed(start)
|
verification_ms = elapsed(start)
|
||||||
reports = []
|
reports = []
|
||||||
for threads in args.threads:
|
for threads in args.threads:
|
||||||
@@ -124,7 +129,7 @@ def native(args):
|
|||||||
try:
|
try:
|
||||||
hello = session.next()
|
hello = session.next()
|
||||||
load_ms = elapsed(session.started)
|
load_ms = elapsed(session.started)
|
||||||
assert hello['device'] == 'cpu' and hello['model'] == manifest['model_id']
|
require(hello['device'] == 'cpu' and hello['model'] == manifest['model_id'], 'Benchmark contract failed at benchmark.py:127')
|
||||||
first, first_ms = session.worker({'op':'query', 'text':'first unseen foreground query',
|
first, first_ms = session.worker({'op':'query', 'text':'first unseen foreground query',
|
||||||
'debug_performance':True})
|
'debug_performance':True})
|
||||||
vectors(first, 1)
|
vectors(first, 1)
|
||||||
@@ -143,8 +148,8 @@ def native(args):
|
|||||||
response, duration = session.worker({'op':'windows','text':passage,'title':'Agreement'})
|
response, duration = session.worker({'op':'windows','text':passage,'title':'Agreement'})
|
||||||
encoded = passage.encode()
|
encoded = passage.encode()
|
||||||
for window in response['windows']:
|
for window in response['windows']:
|
||||||
assert encoded[window['start_byte']:window['end_byte']].decode() == window['text']
|
require(encoded[window['start_byte']:window['end_byte']].decode() == window['text'], 'Benchmark contract failed at benchmark.py:146')
|
||||||
assert response['windows'][-1]['end_byte'] == len(encoded)
|
require(response['windows'][-1]['end_byte'] == len(encoded), 'Benchmark contract failed at benchmark.py:147')
|
||||||
window_samples.append({'bytes':len(encoded),'windows':len(response['windows']),
|
window_samples.append({'bytes':len(encoded),'windows':len(response['windows']),
|
||||||
'round_trip_ms':duration})
|
'round_trip_ms':duration})
|
||||||
documents = [(f'Document {n}. Supplier cancellation requires sixty days written notice. '
|
documents = [(f'Document {n}. Supplier cancellation requires sixty days written notice. '
|
||||||
@@ -158,7 +163,7 @@ def native(args):
|
|||||||
response, batch_ms = session.worker({'op':'documents','texts':documents,'title':'Agreement'})
|
response, batch_ms = session.worker({'op':'documents','texts':documents,'title':'Agreement'})
|
||||||
batched = vectors(response, 8)
|
batched = vectors(response, 8)
|
||||||
cosines = [sum(a*b for a,b in zip(left,right)) for left,right in zip(singles,batched)]
|
cosines = [sum(a*b for a,b in zip(left,right)) for left,right in zip(singles,batched)]
|
||||||
assert min(cosines) > .99999
|
require(min(cosines) > .99999, 'Benchmark contract failed at benchmark.py:161')
|
||||||
reports.append({'threads':threads,'worker_info':hello,'process_load_ms':load_ms,
|
reports.append({'threads':threads,'worker_info':hello,'process_load_ms':load_ms,
|
||||||
'first_query_ms':first_ms,'first_query_worker':first['performance'],
|
'first_query_ms':first_ms,'first_query_worker':first['performance'],
|
||||||
'warm_queries':distribution([sample['round_trip_ms'] for sample in samples]),
|
'warm_queries':distribution([sample['round_trip_ms'] for sample in samples]),
|
||||||
@@ -174,8 +179,8 @@ def native(args):
|
|||||||
'First query precedes warm samples. Documents measure IPC batch, not assumed parallelism.'}
|
'First query precedes warm samples. Documents measure IPC batch, not assumed parallelism.'}
|
||||||
|
|
||||||
def mcp(args):
|
def mcp(args):
|
||||||
assert args.db and args.cli
|
require(args.db and args.cli, 'Benchmark contract failed at benchmark.py:177')
|
||||||
assert args.db.is_file()
|
require(args.db.is_file(), 'Benchmark contract failed at benchmark.py:178')
|
||||||
session = Session([str(args.cli.resolve()),'mcp','--db',str(args.db.resolve()),
|
session = Session([str(args.cli.resolve()),'mcp','--db',str(args.db.resolve()),
|
||||||
'--model-dir',str(args.models.resolve()),'--embedding-worker',str(args.worker.resolve()),
|
'--model-dir',str(args.models.resolve()),'--embedding-worker',str(args.worker.resolve()),
|
||||||
'--cpu-threads','1'])
|
'--cpu-threads','1'])
|
||||||
@@ -185,8 +190,8 @@ def mcp(args):
|
|||||||
startup_ms = elapsed(session.started)
|
startup_ms = elapsed(session.started)
|
||||||
session.send({'jsonrpc':'2.0','method':'notifications/initialized'})
|
session.send({'jsonrpc':'2.0','method':'notifications/initialized'})
|
||||||
stats,_ = session.mcp(2,'tools/call',{'name':'stats','arguments':{}})
|
stats,_ = session.mcp(2,'tools/call',{'name':'stats','arguments':{}})
|
||||||
assert stats['structuredContent']['mode'] == 'hybrid'
|
require(stats['structuredContent']['mode'] == 'hybrid', 'Benchmark contract failed at benchmark.py:188')
|
||||||
assert stats['structuredContent']['worker']['device'] == 'cpu'
|
require(stats['structuredContent']['worker']['device'] == 'cpu', 'Benchmark contract failed at benchmark.py:189')
|
||||||
samples=[]
|
samples=[]
|
||||||
identifier=3
|
identifier=3
|
||||||
cases = [('first_unseen',QUERIES[0],None)]
|
cases = [('first_unseen',QUERIES[0],None)]
|
||||||
@@ -199,11 +204,11 @@ def mcp(args):
|
|||||||
arguments['extension']=extension
|
arguments['extension']=extension
|
||||||
result,duration=session.mcp(identifier,'tools/call',{'name':'search','arguments':arguments})
|
result,duration=session.mcp(identifier,'tools/call',{'name':'search','arguments':arguments})
|
||||||
identifier+=1
|
identifier+=1
|
||||||
assert not result.get('isError'), result
|
require(not result.get('isError'), result)
|
||||||
response=result['structuredContent']
|
response=result['structuredContent']
|
||||||
assert response['mode']=='hybrid'
|
require(response['mode']=='hybrid', 'Benchmark contract failed at benchmark.py:204')
|
||||||
if extension:
|
if extension:
|
||||||
assert all(item['filename'].lower().endswith('.'+extension) for item in response['results'])
|
require(all(item['filename'].lower().endswith('.'+extension) for item in response['results']), 'Benchmark contract failed at benchmark.py:206')
|
||||||
samples.append({'case':case,'query':query,'extension':extension,'request_ms':duration,
|
samples.append({'case':case,'query':query,'extension':extension,'request_ms':duration,
|
||||||
'engine_ms':response['metrics']['total_ms'],
|
'engine_ms':response['metrics']['total_ms'],
|
||||||
'matches':[{'filename':item['filename'],'source':item['source'],
|
'matches':[{'filename':item['filename'],'source':item['source'],
|
||||||
@@ -230,7 +235,8 @@ def main():
|
|||||||
parser.add_argument('--rust-profile',choices=['debug','release','not-applicable'],default='not-applicable')
|
parser.add_argument('--rust-profile',choices=['debug','release','not-applicable'],default='not-applicable')
|
||||||
parser.add_argument('--label',default='')
|
parser.add_argument('--label',default='')
|
||||||
args=parser.parse_args()
|
args=parser.parse_args()
|
||||||
assert 1<=args.repeats<=20 and all(1<=n<=8 for n in args.threads)
|
require(not args.output.exists(), 'Output must be new; preserve previous evidence')
|
||||||
|
require(1<=args.repeats<=20 and all(1<=n<=8 for n in args.threads), 'Benchmark contract failed at benchmark.py:233')
|
||||||
def digest(path):
|
def digest(path):
|
||||||
with path.open('rb') as stream:
|
with path.open('rb') as stream:
|
||||||
return hashlib.file_digest(stream,'sha256').hexdigest()
|
return hashlib.file_digest(stream,'sha256').hexdigest()
|
||||||
@@ -241,7 +247,8 @@ def main():
|
|||||||
'cli_sha256':digest(args.cli) if args.cli else None,
|
'cli_sha256':digest(args.cli) if args.cli else None,
|
||||||
'results':native(args) if args.mode=='native' else mcp(args)}
|
'results':native(args) if args.mode=='native' else mcp(args)}
|
||||||
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,ensure_ascii=False)+'\n',encoding='utf-8')
|
with args.output.open('x', encoding='utf-8') as output:
|
||||||
|
output.write(json.dumps(report,indent=2,ensure_ascii=False)+'\n')
|
||||||
print(f'Saved {args.output}',flush=True)
|
print(f'Saved {args.output}',flush=True)
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__=='__main__':
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from pathlib import Path
|
|||||||
import platform
|
import platform
|
||||||
import statistics
|
import statistics
|
||||||
import time
|
import time
|
||||||
from benchmark import ROOT, Session, vectors
|
from benchmark import ROOT, Session, vectors, require
|
||||||
|
|
||||||
|
|
||||||
def memory(pid):
|
def memory(pid):
|
||||||
@@ -62,9 +62,9 @@ def main():
|
|||||||
entry = manifest["files"][0]
|
entry = manifest["files"][0]
|
||||||
model = args.models / entry["path"]
|
model = args.models / entry["path"]
|
||||||
started = time.perf_counter()
|
started = time.perf_counter()
|
||||||
assert model.stat().st_size == entry["bytes"]
|
require(model.stat().st_size == entry["bytes"], 'Benchmark contract failed at query_context_benchmark.py:65')
|
||||||
with model.open("rb") as stream:
|
with model.open("rb") as stream:
|
||||||
assert hashlib.file_digest(stream, "sha256").hexdigest() == entry["sha256"]
|
require(hashlib.file_digest(stream, "sha256").hexdigest() == entry["sha256"], 'Benchmark contract failed at query_context_benchmark.py:67')
|
||||||
verification_ms = (time.perf_counter() - started) * 1000
|
verification_ms = (time.perf_counter() - started) * 1000
|
||||||
samples = []
|
samples = []
|
||||||
for repeat in range(args.repeats):
|
for repeat in range(args.repeats):
|
||||||
@@ -74,8 +74,8 @@ def main():
|
|||||||
try:
|
try:
|
||||||
hello = session.next()
|
hello = session.next()
|
||||||
startup_ms = (time.perf_counter() - session.started) * 1000
|
startup_ms = (time.perf_counter() - session.started) * 1000
|
||||||
assert hello["device"] == "cpu" and not hello["vulkan_module_loaded"]
|
require(hello["device"] == "cpu" and not hello["vulkan_module_loaded"], 'Benchmark contract failed at query_context_benchmark.py:77')
|
||||||
assert hello["role"] == role and hello["context_tokens"] == (512 if role == "query" else 2048)
|
require(hello["role"] == role and hello["context_tokens"] == (512 if role == "query" else 2048), 'Benchmark contract failed at query_context_benchmark.py:78')
|
||||||
sample = {"repeat": repeat, "role": role, "hello": hello, "startup_ms": startup_ms, "ready_memory": memory(session.process.pid)}
|
sample = {"repeat": repeat, "role": role, "hello": hello, "startup_ms": startup_ms, "ready_memory": memory(session.process.pid)}
|
||||||
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)
|
||||||
@@ -92,8 +92,8 @@ def main():
|
|||||||
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()
|
||||||
assert not rejected["ok"] and not rejected["fatal"]
|
require(not rejected["ok"] and not rejected["fatal"], 'Benchmark contract failed at query_context_benchmark.py:95')
|
||||||
assert "only supports" in rejected["error"]
|
require("only supports" in rejected["error"], 'Benchmark contract failed at query_context_benchmark.py:96')
|
||||||
vectors(session.worker({"op": "code_query", "text": "retry failed requests"})[0], 1)
|
vectors(session.worker({"op": "code_query", "text": "retry failed requests"})[0], 1)
|
||||||
samples.append(sample)
|
samples.append(sample)
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""Benchmark validity checks must survive python -O."""
|
||||||
|
import math
|
||||||
|
import unittest
|
||||||
|
from benchmark import Session, require, vectors
|
||||||
|
|
||||||
|
|
||||||
|
class Contract(unittest.TestCase):
|
||||||
|
def test_bad_vectors_rejected(self):
|
||||||
|
for result in ([], [[1.0]], [[math.nan] * 768], [[0.0] * 768]):
|
||||||
|
with self.subTest(result_length=len(result)), self.assertRaises(ValueError):
|
||||||
|
vectors({"vectors": result}, 1)
|
||||||
|
|
||||||
|
def test_real_shape_normalized_vector_accepted(self):
|
||||||
|
vector = [1.0] + [0.0] * 767
|
||||||
|
self.assertEqual(vectors({"vectors": [vector]}, 1), [vector])
|
||||||
|
|
||||||
|
def test_cpu_contract_cannot_be_bypassed(self):
|
||||||
|
class FakeSession:
|
||||||
|
def send(self, value):
|
||||||
|
pass
|
||||||
|
def next(self):
|
||||||
|
return {"ok": True, "device": "gpu"}
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
Session.worker(FakeSession(), {"op": "query"})
|
||||||
|
|
||||||
|
def test_explicit_guard_survives_optimization(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
require(False, "invalid asset")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in new issue
Block a user