Exercise 2: Reproducing the Isolated Allocation-Cost Comparison — Possible Solution ==================================================================== THE SETUP ------------------------------ dummy_class = WispClass("Dummy") dummy_function = WispFunction("increment", 0, Chunk(), []) dummy_method_closure = Closure(dummy_function, []) dummy_instance = WispInstance(dummy_class) N = 20000 def heavy_bind_n_times(): fake_stack = [dummy_instance] for _ in range(N): fake_upvalue = Upvalue(fake_stack, 0) new_closure = Closure(dummy_method_closure.function, [fake_upvalue]) def light_bind_n_times(): for _ in range(N): bm = BoundMethod(dummy_instance, dummy_method_closure) # each timed with the median of 7 runs RESULT ------------------------------ Heavy (Upvalue + Closure per access): ~0.223 us/access Light (BoundMethod pair per access): ~0.098 us/access Ratio: ~2.27x (individual numbers will vary run to run and machine to machine; the RATIO consistently lands in a similar range across repeats) WHY THE COST COMES FROM ALLOCATION, NOT DISPATCH LOGIC ------------------------------ Neither function in this benchmark does any actual method DISPATCHING at all -- no OP_GET_PROPERTY runs, no find_method() lookup happens, no bytecode executes. Both functions do exactly one thing, N times: construct one or two plain Python objects and then let them go (nothing stores a reference to new_closure or bm anywhere, so each one becomes immediately eligible for Python's own garbage collection right after the loop iteration that created it). The only variable between the two functions is HOW MANY objects, and of what shape, get constructed per iteration: - light_bind_n_times allocates exactly one object: a BoundMethod with two plain attribute assignments. - heavy_bind_n_times allocates TWO objects: an Upvalue (four attributes, per its own __slots__) and a Closure (wrapping it in a list plus a second attribute). This isolates the comparison to pure object-construction overhead -- the real, measurable cost of "how many objects, with how many fields, does each design decision require the VM to build". A method-dispatch mechanism that needs two heavier objects instead of one lighter one pays that construction cost every single time ANY property access resolves to a method, which is exactly why Chapter 9's own design chose the one-object BoundMethod pairing deliberately, rather than reusing Chapter 7's own Closure-plus-Upvalue machinery for something it was never built to optimize for.