← back to index

wasthon — tp_dealloc A/B proof

One page, two phases on the same module, identical workload — a tight loop of _pickle.dumps(obj). The C dumps() creates a Pickler (a refcounted wasthon instance) and Py_DECREFs it internally; that zero-refcount event is the only place tp_dealloc can fire (a Brython del reaches no FinalizationRegistry).

Phase A: runtime.noFree = false — refcount→0 dispatches tp_dealloc → instance freed. Phase B: runtime.noFree = true — refcount→0 does nothing, reproducing the bridge before tp_dealloc existed.

The deterministic, GC-independent signal is refcounts.size (count of live refcounted instances). Phase A ⇒ flat (every Pickler reclaimed); Phase B ⇒ climbs by one per dumps() (instances pile up). Tune with ?iters=20000.

Caveat: the WASM-heap column grows in both phases — pickling also leaks JS-side sentinel handles (a separate problem tp_dealloc structurally can't touch), and HEAPU8.length only grows in coarse steps. That byte axis can't isolate tp_dealloc; refcounts.size is the signal that does.

Loading…

Phase A — tp_dealloc enabled (noFree=false)

iterrefcounts.sizeΔ refcounts handles.sizeWASM (MB)Δ WASM (kB)

Phase B — tp_dealloc disabled (noFree=true)

iterrefcounts.sizeΔ refcounts handles.sizeWASM (MB)Δ WASM (kB)