When adding bookkeeping for correctness or edge cases (deduplication, debug stats, extra tracking structures), make sure the common/fast path doesn’t pay for work it doesn’t need. Defer allocations, extra passes, or per-call checks until they’re actually required, and fall back to a cheap path when the condition that triggers the extra cost is absent or rare.
Before merging such a change, measure both the previously-fast case and the newly-added case with realistic micro-benchmarks (median over multiple runs, release build) to confirm there’s no unacceptable regression. If a benchmark reveals a regression (e.g. allocating a dict/set on every call even when no duplicates/empty slots exist, or iterating a list twice just to get a count), redesign the algorithm so the expensive work is only triggered by the actual condition, not unconditionally.
For example, instead of always allocating a tracking set/dict for every frame to deduplicate local names:
// Bad: always pay the cost, even when no duplicates exist
PyObject *seen = PySet_New(NULL);
for (...) {
// dedup logic touches "seen" on every slot, every call
}
use a cached/lazy marker that skips the extra data structure entirely unless duplicates are known or suspected:
// Good: only allocate/iterate the extra structure when needed
if (FT_ATOMIC_LOAD_CHAR_RELAXED(co->_co_localsplus_dedup) == DEDUP_UNIQUE) {
// skip tracking entirely - fast path
} else {
// only now pay for the dict/set and walk
}
Similarly, avoid doing two full passes over the same data when one pass can produce both results (e.g., compute a count while iterating for debug output instead of iterating once to print and once again to count). Treat any added per-call overhead as a performance regression candidate until proven negligible by measurement.