PEP 510
* make new methods private * make Guard type private and rename it to PyFuncGuard * elaborate potential changes on the Python semantics * explain than other implementations of Python are free to not implement new methods, or implement them as no-op
This commit is contained in:
parent
238df5a182
commit
04b7e969a2
86
pep-0510.txt
86
pep-0510.txt
|
@ -13,8 +13,8 @@ Python-Version: 3.6
|
||||||
Abstract
|
Abstract
|
||||||
========
|
========
|
||||||
|
|
||||||
Add an API to add specialized functions with guards to functions, to
|
Add a private API to CPython to add specialized functions with guards to
|
||||||
support static optimizers respecting the Python semantics.
|
functions, to support static optimizers respecting the Python semantics.
|
||||||
|
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
|
@ -29,10 +29,15 @@ modified at runtime. Implement optimizations respecting the Python
|
||||||
semantics requires to detect when "something changes", we will call these
|
semantics requires to detect when "something changes", we will call these
|
||||||
checks "guards".
|
checks "guards".
|
||||||
|
|
||||||
This PEP proposes to add a ``specialize()`` method to functions to add a
|
This PEP proposes to add an API to add specialized functions with guards
|
||||||
specialized functions with guards. When the function is called, the
|
to a function. When the function is called, the specialized function is
|
||||||
specialized function is used if nothing changed, otherwise use the
|
used if nothing changed, otherwise use the original bytecode.
|
||||||
original bytecode.
|
|
||||||
|
Even if guards help to respect most parts of the Python semantics, it's
|
||||||
|
really hard to optimize Python without making subtle changes on the
|
||||||
|
exact behaviour. CPython has a long history and many applications rely
|
||||||
|
on implementation details. A compromise must be found between
|
||||||
|
"everything is mutable" and performance.
|
||||||
|
|
||||||
Writing an optimizer is out of the scope of this PEP.
|
Writing an optimizer is out of the scope of this PEP.
|
||||||
|
|
||||||
|
@ -112,18 +117,18 @@ Replace ``chr(65)`` with ``"A"``::
|
||||||
def fast_func():
|
def fast_func():
|
||||||
return "A"
|
return "A"
|
||||||
|
|
||||||
func.specialize(fast_func.__code__, [myoptimizer.GuardBuiltins("chr")])
|
func._specialize(fast_func.__code__, [myoptimizer.GuardBuiltins("chr")])
|
||||||
del fast_func
|
del fast_func
|
||||||
|
|
||||||
print("func(): %s" % func())
|
print("func(): %s" % func())
|
||||||
print("#specialized: %s" % len(func.get_specialized()))
|
print("#specialized: %s" % len(func._get_specialized()))
|
||||||
print()
|
print()
|
||||||
|
|
||||||
import builtins
|
import builtins
|
||||||
builtins.chr = lambda obj: "mock"
|
builtins.chr = lambda obj: "mock"
|
||||||
|
|
||||||
print("func(): %s" % func())
|
print("func(): %s" % func())
|
||||||
print("#specialized: %s" % len(func.get_specialized()))
|
print("#specialized: %s" % len(func._get_specialized()))
|
||||||
|
|
||||||
Output::
|
Output::
|
||||||
|
|
||||||
|
@ -157,10 +162,10 @@ to the builtin ``chr()`` function::
|
||||||
def func(arg):
|
def func(arg):
|
||||||
return chr(arg)
|
return chr(arg)
|
||||||
|
|
||||||
func.specialize(chr, [myoptimizer.GuardBuiltins("chr")])
|
func._specialize(chr, [myoptimizer.GuardBuiltins("chr")])
|
||||||
|
|
||||||
print("func(65): %s" % func(65))
|
print("func(65): %s" % func(65))
|
||||||
print("#specialized: %s" % len(func.get_specialized()))
|
print("#specialized: %s" % len(func._get_specialized()))
|
||||||
print()
|
print()
|
||||||
|
|
||||||
import builtins
|
import builtins
|
||||||
|
@ -222,24 +227,43 @@ guards::
|
||||||
Changes
|
Changes
|
||||||
=======
|
=======
|
||||||
|
|
||||||
* Add two new methods to functions:
|
* Add two new private methods to functions:
|
||||||
|
|
||||||
- ``specialize(code, guards: list)``: add specialized
|
* ``_specialize(code, guards: list)``: add specialized
|
||||||
function with guard. `code` is a code object (ex:
|
function with guard. `code` is a code object (ex:
|
||||||
``func2.__code__``) or any callable object (ex: ``len``).
|
``func2.__code__``) or any callable object (ex: the builtin
|
||||||
The specialization can be ignored if a guard already fails.
|
``len()`` function). The specialization can be ignored if a guard
|
||||||
- ``get_specialized()``: get the list of specialized functions with
|
already fails or for other reasons (ex: the implementation of Python
|
||||||
guards
|
does not implement this feature). Return ``False`` is the
|
||||||
|
specialized function was ignored, return ``True`` otherwise.
|
||||||
|
|
||||||
* Base ``Guard`` type which can be used as parent type to implement
|
* ``_get_specialized()``: get the list of specialized functions with
|
||||||
guards. It requires to implement a ``check()`` function, with an
|
guards. Return a list of ``(func, guards)`` tuples where func is the
|
||||||
optional ``first_check()`` function. API:
|
specialized function and guards is a list of guards. Return an empty
|
||||||
|
list if the function was never specialized.
|
||||||
|
|
||||||
|
* Add a private ``PyFuncGuard`` Python type. It requires to implement a
|
||||||
|
C ``check()`` function, with an optional C ``init()`` function. API:
|
||||||
|
|
||||||
|
* ``int init(PyObject *guard, PyObject *func)``: initialize a guard,
|
||||||
|
*func* is the function to which the specialized function will be
|
||||||
|
attached. Result:
|
||||||
|
|
||||||
|
* return ``1`` on success
|
||||||
|
* return ``0`` if the guard will always fail (the specialization must be
|
||||||
|
ignored)
|
||||||
|
* raise an exception and return ``-1`` on error
|
||||||
|
|
||||||
* ``int first_check(PyObject *guard, PyObject *func)``: return 0 on
|
|
||||||
success, -1 if the guard will always fail
|
|
||||||
* ``int check(PyObject *guard, PyObject **stack, int na, int nk)``:
|
* ``int check(PyObject *guard, PyObject **stack, int na, int nk)``:
|
||||||
return 1 on success, 0 if the guard failed temporarely, -1 if the
|
check the guard. Result:
|
||||||
guard will always fail
|
|
||||||
|
* return 2 on success
|
||||||
|
* return 1 if the guard failed temporarely
|
||||||
|
* return 0 if the guard will always fail
|
||||||
|
* raise an exception and return -1 on error
|
||||||
|
|
||||||
|
* A guard can be called in Python with parameters, it returns the
|
||||||
|
result of the guard check.
|
||||||
|
|
||||||
Microbenchmark on ``python3.6 -m timeit -s 'def f(): pass' 'f()'`` (best
|
Microbenchmark on ``python3.6 -m timeit -s 'def f(): pass' 'f()'`` (best
|
||||||
of 3 runs):
|
of 3 runs):
|
||||||
|
@ -263,6 +287,20 @@ are not stored in ``.pyc`` files but created and registered at runtime,
|
||||||
when a module is loaded.
|
when a module is loaded.
|
||||||
|
|
||||||
|
|
||||||
|
Other implementations of Python
|
||||||
|
===============================
|
||||||
|
|
||||||
|
This PEP is designed to be implemented in C for CPython.
|
||||||
|
|
||||||
|
Other implementations of Python are free to not implement added private
|
||||||
|
function methods.
|
||||||
|
|
||||||
|
Or they can implement a ``_specialize()`` method which always ignores
|
||||||
|
the specialized function (in short, do nothing and always return
|
||||||
|
``False``) and a ``_get_specialized()`` method which always returns an
|
||||||
|
empty list.
|
||||||
|
|
||||||
|
|
||||||
Discussion
|
Discussion
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue