[Python-Dev] MAKE_FUNCTION simplification (original) (raw)

Guido van Rossum guido at python.org
Thu Apr 14 11:27:52 EDT 2016


Great analysis! What might stand in the way of adoption is concern for bytecode manipulation libraries that would have to be changed. What might encourage adoption would be a benchmark showing this saves a lot of time.

Personally I'm expecting it won't make much of a difference for real programs since almost always the cost of creating the function is dwarfed by the (total) cost of running it. But Python does create a lot of functions, and there's also lambdas.

There's also talk of switching to wordcode, in a different thread. Maybe the idea would be easier to introduce there? (Bytecode libraries would have to change anyways, so the additional concern for this change would be minimal.)

On Thu, Apr 14, 2016 at 2:04 AM, Nikita Nemkin <nikita at nemkin.ru> wrote:

MAKEFUNCTION opcode is complex due to the way it receives input arguments:

1) default args, individually; 2) default kwonly args, individual name-value pairs; 3) a tuple of parameter names (single constant); 4) annotation values, individually; 5) code object; 6) qualname. The counts for 1,2,4 are packed into oparg bitfields, making oparg large. My suggestion is to pre-package 1-4 before calling MAKEFUNCTION, i.e. explicitly emit BUILDTUPLE for defaults args and BUILDMAPs for keyword defaults and annotations. Then, MAKEFUNCTION will become a dramatically simpler 5 argument opcode, taking 1) default args tuple (optional); 2) default keyword only args dict (optional); 3) annotations dict (optional); 4) code object; 5) qualname. These arguments correspond exactly to annotations, kwdefaults, defaults, code and qualname attributes. For optional args, oparg bits should indicate individual arg presence. (This also saves None checks in opcode implementation.) If we add another optional argument (and oparg bit) for closure attribute, then separate MAKECLOSURE opcode becomes unnecessary. Default args tuple is likely to be a constant and can be packaged whole, compensating for the extra size of explicit BUILD* instructions. Compare the current implementation: https://github.com/python/cpython/blob/master/Python/ceval.c#L3262 with this provisional implementation (untested): TARGET(MAKEFUNCTION) { PyObject *qualname = POP(); PyObject *codeobj = POP(); PyFunctionObject *func; func = (PyFunctionObject *)PyFunctionNewWithQualName( codeobj, f->fglobals, qualname); PyDECREF(codeobj); PyDECREF(qualname); if (func == NULL) goto error; /* NB: PyNone is not an acceptable value for these. */ if (oparg & 0x08) func->funcclosure = POP(); if (oparg & 0x04) func->funcannotations = POP(); if (oparg & 0x02) func->funckwdefaults = POP(); if (oparg & 0x01) func->funcdefaults = POP(); PUSH((PyObject *)func); DISPATCH(); } compile.c also gets a bit simpler, but not much. What do you think?


Python-Dev mailing list Python-Dev at python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/guido%40python.org

-- --Guido van Rossum (python.org/~guido)



More information about the Python-Dev mailing list