A Python object holding five float fields is created 5 million times and its fields are read in a tight loop. Explain where the memory and the time go in an ordinary class, what a slots declaration changes and by how much, and the two things it takes away.
A Python object holding five float fields is created 5 million times and its fields are read in a tight loop. Explain where the memory and the time go in an ordinary class, what a slots declaration changes and by how much, and the two things it takes away.
Approach: Count the per-instance overhead of the instance dictionary, then follow every step an attribute read performs before it reaches the value.
An ordinary class keeps fields in a per-instance dictionary costing roughly 100 to 160 bytes on top of the object header, and every attribute lookup hashes the name and probes that dictionary after checking the type for a data descriptor, while a slots declaration replaces the dictionary with fixed offsets, cutting the instance to about 80 bytes and turning the read into a descriptor that loads a known slot index, giving 3 to 5 times less per-instance memory and 20 to 30 percent faster reads. Across 5 million instances that is a few hundred megabytes. What it takes away is assigning an attribute that was never declared, which breaks anything patching instances at runtime, and the instance dictionary itself, which breaks code calling vars on the object. Inheriting from a base without slots reinstates the dictionary and silently removes the saving, which is the usual reason the change measures as no change.
Follow-up: How would you keep the compact layout while allowing a small subset of instances to carry extra fields?
Key concepts: instance dictionary, attribute lookup, descriptor, per-instance memory.