[Python-Dev] On the METH_FASTCALL calling convention

Jeroen Demeyer J.Demeyer at UGent.be
Thu Jul 5 10:53:05 EDT 2018

Hello all,

As discussed in some other threads ([1], [2]), we should discuss the 
METH_FASTCALL calling convention.

For passing only positional arguments, a C array of Python objects is 
used, which is as fast as it can get. When the Python interpreter calls 
a function, it builds that C array on the interpreter stack:

 >>> from dis import dis
 >>> def f(x, y): return g(x, y, 12)
 >>> dis(f)
   1           0 LOAD_GLOBAL              0 (g)
               2 LOAD_FAST                0 (x)
               4 LOAD_FAST                1 (y)
               6 LOAD_CONST               1 (12)
               8 CALL_FUNCTION            3
              10 RETURN_VALUE

A C array can also easily and efficiently be handled by the C function 
receiving it. So I consider this uncontroversial.

The convention for METH_FASTCALL|METH_KEYWORDS is that keyword *names* 
are passed as a tuple and keyword *values* in the same C array with 
positional arguments. An example:

 >>> from dis import dis
 >>> def f(x, y, z): return f(x, foo=y, bar=z)
 >>> dis(f)
   1           0 LOAD_GLOBAL              0 (f)
               2 LOAD_FAST                0 (x)
               4 LOAD_FAST                1 (y)
               6 LOAD_FAST                2 (z)
               8 LOAD_CONST               1 (('foo', 'bar'))
              10 CALL_FUNCTION_KW         3
              12 RETURN_VALUE

This is pretty clever: it exploits the fact that ('foo', 'bar') is a 
constant tuple stored in f.__code__.co_consts. Also, a tuple can be 
efficiently handled by the called code: it is essentially a thin wrapper 
around a C array of Python objects. So this works well.

The only case when this handling of keywords is suboptimal is when using 
**kwargs. In that case, a dict must be converted to a tuple. It looks 
hard to me to support efficiently both the case of fixed keyword 
arguments (f(foo=x)) and a keyword dict (f(**kwargs)). Since the former 
is more common than the latter, the current choice is optimal.

In other words: I see nothing to improve in the calling convention of 
METH_FASTCALL. I suggest to keep it and make it public as-is.


[1] https://mail.python.org/pipermail/python-dev/2018-June/153945.html
[2] https://mail.python.org/pipermail/python-dev/2018-July/154251.html

More information about the Python-Dev mailing list