This would almost certainly break my code. As a DSL developer I do a lot of (exec | eval | introspection | ... ) shenanigans, that would make doing liveness analysis undecidable.
On Apr 8, 2020, at 09:57, Guido van Rossum <email@example.com> wrote:
> Look at the following code.
> def foo(a, b):
> x = a + b
> if not x:
> return None
> sleep(1) # A calculation that does not use x
> return a*b
> This code DECREFs x when the frame is exited (at the return statement). But (assuming) we can clearly see that x is not needed during the sleep (representing a big calculation), we could insert a "del x" statement before the sleep.
> I think our compiler is smart enough to find out *some* cases where it could safely insert such del instructions.
It depends on how much you’re willing to break and still call it “safely”.
store = inspect.current_frame().f_back.f_locals['x']
This is a ridiculous example, but it shows that you can’t have all of Python’s dynamic functionality and still know when locals are dead. And there are less ridiculous examples with different code. If foo actually calls eval, exec, locals, vars, etc., or if it has a nested function that nonlocals x, etc., how can we spot that at compile time and keep x alive?
Maybe that’s ok. After all, that code doesn’t work in a Python implementation that doesn’t have stack frame support. Some of the other possibilities might be more portable, but I don’t know without digging in further.
Or maybe you can add new restrictions to what locals and eval and so on guarantee that will make it ok? Some code will break, but only rare “expert” code, where the authors will know how to work around it.
Or, if not, it’s definitely fine as an opt-in optimization: decorate the function with @deadlocals and that decorator scans the bytecode and finds any locals that are dead assuming there’s no use of locals/eval/cells/etc. and, because you told it to assume that by opting in to the decorator, it can insert a DELETE_FAST safely.
People already do similar things today—e.g., I’ve (only once in live code, but that’s still more than zero) used a @fastconst decorator that turns globals into consts on functions that I know are safe and are bottlenecks, and this would be no different. And of course you can add a recursive class decorator, or an import hook (or maybe even a command line flag or something) that enables it everywhere (maybe with a @nodeadlocals decorator for people who want it _almost_ everywhere but need to opt out one or two functions).
Did Victor Stinner explore this as one of the optimizations for FAT Python/PEP 511/etc.? Maybe not, since it’s not something you can insert a guard, speculatively do, and then undo if the guard triggers, which was I think his key idea.
Python-ideas mailing list -- firstname.lastname@example.org
To unsubscribe send an email to email@example.com
Message archived at https://firstname.lastname@example.org/message/OIGCRV464VJW3FRRBBK25XSNQYGWID7N/
Code of Conduct: http://python.org/psf/codeofconduct/