Closes the three tractable pending buckets (all non-no_dir work):
+ lean4-0004..0007: 4 correctness/race benches (ir_interp DCL, jobreg
IO.Ref race, g_opts thread-local leakage, process envvar hash).
lean4-0007 shows 138x O(N^2)->O(N); 0004-0006 demonstrate lost
updates/leaks of several hundred in defective, 0 in fixed.
+ 0ad-0001..0004: 3 CWE-407 list.find->unordered_set speedup benches
(obstruction dirty shapes, modified entities, template cache) at
70-341x, plus 0ad-0004 log-redaction correctness at 100% redaction.
+ activemq-0001..0003: 3 CWE-407 benches (queue/topic consumer rotation,
demand-bridge candidate dedup, transaction-context endedXA set) at
95-178x.
+ linux-0001..0008: 8 Python complexity-class models for the kernel
patches. Coexist with the existing build-and-bench.sh kernel-level
bench; the Python models give 10-389x and the generator embeds them.
+ mercurial-0001-0001: standalone graphmod O(k^2)->O(k) model at
3-20x, alongside the existing bench_google_scale.py (which imports
the real mercurial graphmod).
Progress: 13 -> 33 full coverage. Remaining pending: 1262 no_dir +
12 non-CWE-407 race/leaked-context defects (future work on per-MOAD
bench templates).
91 lines
3.1 KiB
Python
91 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
# bench-lean4-0004.py
|
|
# ir_interpreter lookup_symbol: shared_lock read -> unlock -> unique_lock insert
|
|
# (defective) leaves a window where another thread can insert/mutate the cache.
|
|
# Fixed path: single unique_lock acquisition. Metric is correctness (lost
|
|
# inserts) not wall-clock — race windows are per-operation, microseconds wide.
|
|
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
|
|
def bench_defective(n_threads, ops_per_thread):
|
|
"""Model: read-without-lock → shared_lock → unlock → unique_lock → insert.
|
|
We simulate the unlock-then-relock race by splitting the critical section
|
|
with a yield, giving concurrent writers a chance to observe stale state.
|
|
"""
|
|
cache = {}
|
|
lock = threading.Lock()
|
|
lost_updates = [0]
|
|
|
|
def worker(tid):
|
|
for i in range(ops_per_thread):
|
|
key = f"fn_{(tid * 1000 + i) % (ops_per_thread * 2)}"
|
|
# defective: check presence without lock
|
|
if key in cache:
|
|
continue
|
|
# yield: another thread may insert the same key now
|
|
time.sleep(0)
|
|
with lock:
|
|
if key in cache:
|
|
# We raced: another thread inserted. Lost update if we
|
|
# were about to compute and store a value.
|
|
lost_updates[0] += 1
|
|
else:
|
|
cache[key] = tid
|
|
|
|
t0 = time.perf_counter()
|
|
threads = [threading.Thread(target=worker, args=(i,)) for i in range(n_threads)]
|
|
for t in threads: t.start()
|
|
for t in threads: t.join()
|
|
elapsed = time.perf_counter() - t0
|
|
return elapsed, lost_updates[0]
|
|
|
|
|
|
def bench_fixed(n_threads, ops_per_thread):
|
|
"""Single unique_lock acquisition — no unlock-relock window."""
|
|
cache = {}
|
|
lock = threading.Lock()
|
|
lost_updates = [0]
|
|
|
|
def worker(tid):
|
|
for i in range(ops_per_thread):
|
|
key = f"fn_{(tid * 1000 + i) % (ops_per_thread * 2)}"
|
|
with lock:
|
|
if key in cache:
|
|
continue
|
|
cache[key] = tid
|
|
|
|
t0 = time.perf_counter()
|
|
threads = [threading.Thread(target=worker, args=(i,)) for i in range(n_threads)]
|
|
for t in threads: t.start()
|
|
for t in threads: t.join()
|
|
elapsed = time.perf_counter() - t0
|
|
return elapsed, lost_updates[0]
|
|
|
|
|
|
TRIALS = 3
|
|
|
|
|
|
def run():
|
|
lines = []
|
|
header = "=== lean4-0004: ir_interpreter double-checked lock race (correctness) ==="
|
|
print(header); lines.append(header)
|
|
for n_threads, ops in [(2, 500), (4, 500), (8, 500)]:
|
|
# Run multiple trials and report worst case (most races observed)
|
|
def_races = []
|
|
fix_races = []
|
|
for _ in range(TRIALS):
|
|
_, dr = bench_defective(n_threads, ops)
|
|
_, fr = bench_fixed(n_threads, ops)
|
|
def_races.append(dr); fix_races.append(fr)
|
|
line = (f"threads={n_threads:<2} ops={ops:<4}: "
|
|
f"defective_lost_updates(max)={max(def_races):>4} "
|
|
f"fixed_lost_updates(max)={max(fix_races):>4}")
|
|
print(line); lines.append(line); sys.stdout.flush()
|
|
return lines
|
|
|
|
|
|
if __name__ == "__main__":
|
|
run()
|