4 ms·
Yes, but your example is less efficient than the new code. Remember: every variable is an assignment into a dict, and every lookup a query into a dict. Reduci
by hermitdev 7y ago
Yes, but your example is less efficient than the new code.
Remember: every variable is an assignment into a dict, and every lookup a query into a dict. Reducing name lookups and assignments can yield good speedup in tight loops. For instance, caching os.path.join, os.path.split into local names can significantly speed up tight loops iterating over a filesystem. For example, os.path.split is potentially 5 dictionary lookups. Checking locals, nonlocals and globals for os. Then another to find path, and a final one for split. And this happens at runtime, for every invocation.
- murkt 7y agoLocal variables aren’t stored in a dict, likewise when a class has defined __slots__. Globals, modules and usual classes do use dicts internally, but locals do not. So from the efficiency standpoint it’s (almost?) the same. I haven’t checked the bytecode, maybe there is some slight difference.
- hermitdev 7y agoIt has been a while since I've looked at the implementation details. How does locals() work, then? It does return a dict. Slots are definitely an edge case I did not address. I honestly dont know how name lookup works in any version of Python. Edit: I realize that local name lookup doesn't need to follow the result of the locals() built-in function.
- jakear 7y agoI checked the bytecode, in the walrus version you get [...DUP_TOP, STORE_FAST, ...] in the non-walrus you get [...STORE_FAST, LOAD_FAST, ...]. Besides that identical. I imagine DUP_TOP is faster than LOAD_FAST, but I feel either way this is useless micro optimization at its finest.
- jakear 7y agoIn cpython: DUP_TOP: PyObject *top = TOP(); Py_INCREF(top); PUSH(top); FAST_DISPATCH(); TOP: (stack_pointer[-1]) LOAD_FAST: PyObject *value = GETLOCAL(oparg); if (value == NULL) { /* throw */ } Py_INCREF(value); PUSH(value); FAST_DISPATCH(); GETLOCAL: (fastlocals[i]) So looks like either way, you have an array access of something definitely in cache, a Py_INCREF, a PUSH, and a FAST_DISPATCH. The walrus operator saves you a null-check, but that check is probably skipped right over by the branch predictor, as it always throws. I'd bet the performance is indistinguishable, but I'd be interested to see for real.