* 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:
Victor Stinner 2016-01-11 23:55:32 +01:00
parent 238df5a182
commit 04b7e969a2
1 changed files with 62 additions and 24 deletions

View File

@ -13,8 +13,8 @@ Python-Version: 3.6
Abstract
========
Add an API to add specialized functions with guards to functions, to
support static optimizers respecting the Python semantics.
Add a private API to CPython to add specialized functions with guards to
functions, to support static optimizers respecting the Python semantics.
Rationale
@ -29,10 +29,15 @@ modified at runtime. Implement optimizations respecting the Python
semantics requires to detect when "something changes", we will call these
checks "guards".
This PEP proposes to add a ``specialize()`` method to functions to add a
specialized functions with guards. When the function is called, the
specialized function is used if nothing changed, otherwise use the
original bytecode.
This PEP proposes to add an API to add specialized functions with guards
to a function. When the function is called, the specialized function is
used if nothing changed, otherwise use the 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.
@ -112,18 +117,18 @@ Replace ``chr(65)`` with ``"A"``::
def fast_func():
return "A"
func.specialize(fast_func.__code__, [myoptimizer.GuardBuiltins("chr")])
func._specialize(fast_func.__code__, [myoptimizer.GuardBuiltins("chr")])
del fast_func
print("func(): %s" % func())
print("#specialized: %s" % len(func.get_specialized()))
print("#specialized: %s" % len(func._get_specialized()))
print()
import builtins
builtins.chr = lambda obj: "mock"
print("func(): %s" % func())
print("#specialized: %s" % len(func.get_specialized()))
print("#specialized: %s" % len(func._get_specialized()))
Output::
@ -157,10 +162,10 @@ to the builtin ``chr()`` function::
def func(arg):
return chr(arg)
func.specialize(chr, [myoptimizer.GuardBuiltins("chr")])
func._specialize(chr, [myoptimizer.GuardBuiltins("chr")])
print("func(65): %s" % func(65))
print("#specialized: %s" % len(func.get_specialized()))
print("#specialized: %s" % len(func._get_specialized()))
print()
import builtins
@ -222,24 +227,43 @@ guards::
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:
``func2.__code__``) or any callable object (ex: ``len``).
The specialization can be ignored if a guard already fails.
- ``get_specialized()``: get the list of specialized functions with
guards
``func2.__code__``) or any callable object (ex: the builtin
``len()`` function). The specialization can be ignored if a guard
already fails or for other reasons (ex: the implementation of Python
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
guards. It requires to implement a ``check()`` function, with an
optional ``first_check()`` function. API:
* ``_get_specialized()``: get the list of specialized functions with
guards. Return a list of ``(func, guards)`` tuples where func is the
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)``:
return 1 on success, 0 if the guard failed temporarely, -1 if the
guard will always fail
check the guard. Result:
* 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
of 3 runs):
@ -263,6 +287,20 @@ are not stored in ``.pyc`` files but created and registered at runtime,
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
==========