GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — C API: What's the status of the half-private FASTCALL calling convention? · Issue #106023 · python/cpython

C API: What's the status of the half-private FASTCALL calling convention? #106023

New issue
Copy link
New issue
Copy link
Closed
Closed
C API: What's the status of the half-private FASTCALL calling convention?#106023
Copy link
Labels
topic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@vstinner
vstinner
opened on Jun 23, 2023
Issue body actions

Hi,

In 2017, I added a new experimental FASTCALL calling convention. See my articles about it: The start of the FASTCALL project and FASTCALL microbenchmarks. It avoids the need to create a temporary tuple to pass positional arguments and the need to create a temporary dictionary to pass keyword arguments. I did my best to keep this API private. I added functions prefixed with _Py: _PyObject_Fastcall(). Cython is eager to always use the fastest code and quickly adopted this new METH_FASTCALL calling convention... oops, I forgot to add a _Py prefix since these METH constants don't start with Py.

In 2019, this calling convention was extended to support also method calls (pass the self argument): PEP 590 – Vectorcall: a fast calling protocol for CPython. This new API is public and standardized. For example, it added public PyVectorcall_Function() and PyObject_Vectorcall() functions.

In 2023, the FASTCALL API is still around in the public C API:

  • METH_FASTCALL
  • _PyObject_FastCall()
  • _PyObject_FastCallTstate()
  • _PyObject_FastCallDict()
  • _PyObject_FastCallDictTstate()
  • _PyStack_AsDict()
  • _PY_FASTCALL_SMALL_STACK

Can it be deprecated? Removed? I suppose that if we are in the unknown, the safe option is to start by deprecating it in Python 3.13 and plan its removal in Python 3.15.

cc @encukou

Victor

Linked PRs

  • gh-106023: Update code using _PyObject_FastCall() #106257
  • gh-106023: Remove _PY_FASTCALL_SMALL_STACK constant #106258
  • gh-106023: Rename _PyObject_FastCallDictTstate() #106264
  • gh-106023: Remove _PyObject_FastCall() function #106265
  • gh-106023: Remove _PyObject_FastCallTstate() function #106273
  • gh-106023: Update What's New in 3.13: _PyObject_FastCall() #117633
  • Revert "gh-106023: Update What's New in 3.13: _PyObject_FastCall() (#117633)" #117676

Activity

Next
  1. vstinner
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Jun 23, 2023
  2. AlexWaygood
    added
    topic-C-API
    on Jun 23, 2023
  3. encukou commented on Jun 27, 2023

    @encukou
    encukou
    on Jun 27, 2023
    Member
    More actions

    Does vectorcall replace this completely? If that's the case, let's first remove all uses from CPython, and then decide.

    If not, then it's probably genuinely useful for Cython or others. Let's move it to unstable API in that case. (Or even public?)

    _PY_FASTCALL_SMALL_STACK is internal detail that's easy for users to replace (possibly with a more appropriate value). IMO it's safe to move to private headers.

  4. vstinner
    added 4 commits that reference this issue on Jun 30, 2023

    pythongh-106023: Update code using _PyObject_FastCall()

    7e90732

    pythongh-106023: Update code using _PyObject_FastCall()

    b6a0230

    pythongh-106023: Update code using _PyObject_FastCall()

    0c16846

    pythongh-106023: Remove _PY_FASTCALL_SMALL_STACK constant

    c0ba650
  5. vstinner commented on Jun 30, 2023

    @vstinner
    vstinner
    on Jun 30, 2023
    MemberAuthor
    More actions

    Does vectorcall replace this completely? If that's the case, let's first remove all uses from CPython, and then decide.

    _PyObject_FastCall(func, nargs, args) can be safely replaced with PyObject_Vectorcall(func, nargs, args, NULL).

    PR #106257 removes _PyObject_FastCall() usage in CPython: replace it with PyObject_Vectorcall().

    _PY_FASTCALL_SMALL_STACK is internal detail that's easy for users to replace (possibly with a more appropriate value). IMO it's safe to move to private headers.

    PR #106258 removes the constant from the public C API.

  6. vstinner
    added 2 commits that reference this issue on Jun 30, 2023

    gh-106023: Update code using _PyObject_FastCall() (#106257)

    8c5f74f

    gh-106023: Remove _PY_FASTCALL_SMALL_STACK constant (#106258)

    cd58091
  7. vstinner commented on Jun 30, 2023

    @vstinner
    vstinner
    on Jun 30, 2023
    MemberAuthor
    More actions

    In 2019, a commit implementing PEP 590 removed _PyObject_FastCallKeywords().

    My recent commit 00e75a3 removed _PyObject_FastCallDict(). Oh, I didn't notice that I was removing a C function of the FastCall family. I planned to handle these differently.

  8. vstinner commented on Jun 30, 2023

    @vstinner
    vstinner
    on Jun 30, 2023
    MemberAuthor
    More actions

    On PyPI top 5,000 projects, 10 projects use it. 3 of them are related to pydevd or copies of pydevd.

    Usage:

    PYPI-2023-04-13/scipy-1.10.1.tar.gz: scipy-1.10.1/scipy/_lib/_uarray/vectorcall.cxx: return _PyObject_FastCallKeywords(
    PYPI-2023-04-13/scipy-1.10.1.tar.gz: scipy-1.10.1/scipy/_lib/_uarray/vectorcall.cxx: return _PyObject_FastCallDict(callable, (PyObject **)args, nargs, kwdict);
    PYPI-2023-04-13/mypy-1.2.0.tar.gz: mypy-1.2.0/mypyc/lib-rt/pythonsupport.h: new_base = _PyObject_FastCall(meth, stack, 1);
    PYPI-2023-04-13/mypy-1.2.0.tar.gz: mypy-1.2.0/mypyc/lib-rt/pythonsupport.h: super = _PyObject_FastCall((PyObject *)&PySuper_Type, args, 2);
    PYPI-2023-04-13/mypy-1.2.0.tar.gz: mypy-1.2.0/mypyc/lib-rt/pythonsupport.h: result = _PyObject_FastCallDict(func, NULL, 0, kwds);
    PYPI-2023-04-13/orjson-3.8.10.tar.gz: orjson-3.8.10/local_dependencies/pyo3-ffi/src/cpython/abstract_.rs: pub unsafe fn _PyObject_FastCallTstate(
    PYPI-2023-04-13/orjson-3.8.10.tar.gz: orjson-3.8.10/local_dependencies/pyo3-ffi/src/cpython/abstract_.rs: pub unsafe fn _PyObject_FastCall(
    PYPI-2023-04-13/orjson-3.8.10.tar.gz: orjson-3.8.10/local_dependencies/pyo3-ffi/src/cpython/abstract_.rs: _PyObject_FastCallTstate(PyThreadState_GET(), func, args, nargs)
    PYPI-2023-04-13/orjson-3.8.10.tar.gz: orjson-3.8.10/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs: pub unsafe fn _PyObject_FastCallTstate(
    PYPI-2023-04-13/orjson-3.8.10.tar.gz: orjson-3.8.10/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs: pub unsafe fn _PyObject_FastCall(
    PYPI-2023-04-13/orjson-3.8.10.tar.gz: orjson-3.8.10/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs: _PyObject_FastCallTstate(PyThreadState_GET(), func, args, nargs)
    PYPI-2023-04-13/typed_ast-1.5.4.tar.gz: typed_ast-1.5.4/ast3/Python/ast.c: _PyObject_FastCall(PyObject *func, PyObject *const *args, int nargs)
    PYPI-2023-04-13/typed_ast-1.5.4.tar.gz: typed_ast-1.5.4/ast3/Python/ast.c: id2 = _PyObject_FastCall(c->c_normalize, args, 2);
    PYPI-2023-04-13/gevent-22.10.2.tar.gz: gevent-22.10.2/src/gevent/_generated_include/PyObjectFastCall_impl_f6ad1ccc7f137e4637d2bbaa621a6295ceb3138b.h: static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs) {
    PYPI-2023-04-13/gevent-22.10.2.tar.gz: gevent-22.10.2/src/gevent/_generated_include/PyObjectFastCall_impl_f6ad1ccc7f137e4637d2bbaa621a6295ceb3138b.h: static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t _nargs, PyObject *kwargs) {
    PYPI-2023-04-13/gevent-22.10.2.tar.gz: gevent-22.10.2/src/gevent/_generated_include/PyObjectFastCall_impl_f6ad1ccc7f137e4637d2bbaa621a6295ceb3138b.h: return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs);
    PYPI-2023-04-13/iteration_utilities-0.11.0.tar.gz: iteration_utilities-0.11.0/src/iteration_utilities/_iteration_utilities/helper.h: return _PyObject_FastCall(callable, args, 1);
    PYPI-2023-04-13/iteration_utilities-0.11.0.tar.gz: iteration_utilities-0.11.0/src/iteration_utilities/_iteration_utilities/helper.h: return _PyObject_FastCall(callable, args, 2);
    
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: PyObject_FastCallDictCustom(PyObject* callback, PyObject *stack[3], int ignoredStackSizeAlways3, void* ignored)
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: // result = _PyObject_FastCall(callback, stack, 3);
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: // Note that _PyObject_FastCall is actually a define:
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: // #define _PyObject_FastCall(func, args, nargs) _PyObject_FastCallDict((func), (args), (nargs), NULL)
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_custom_pyeval_settrace_common.hpp: _PyObject_FastCallDict* pyObject_FastCallDict;
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp: DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp: DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "PyObject_VectorcallDict", 533);
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp: // we have to use PyObject_FastCallDictCustom for older versions of CPython (pre 3.7).
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp: pyObject_FastCallDict = reinterpret_cast<_PyObject_FastCallDict*>(&PyObject_FastCallDictCustom);
    PYPI-2023-04-13/debugpy-1.6.7.zip: debugpy-1.6.7/src/debugpy/_vendored/pydevd/pydevd_attach_to_process/common/py_utils.hpp: typedef PyObject * (_PyObject_FastCallDict)(
    
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: PyObject_FastCallDictCustom(PyObject* callback, PyObject *stack[3], int ignoredStackSizeAlways3, void* ignored)
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: // result = _PyObject_FastCall(callback, stack, 3);
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: // Note that _PyObject_FastCall is actually a define:
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_custom_pyeval_settrace.hpp: // #define _PyObject_FastCall(func, args, nargs) _PyObject_FastCallDict((func), (args), (nargs), NULL)
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_custom_pyeval_settrace_common.hpp: _PyObject_FastCallDict* pyObject_FastCallDict;
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_settrace.hpp: DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_settrace.hpp: DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "PyObject_VectorcallDict", 533);
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_settrace.hpp: // we have to use PyObject_FastCallDictCustom for older versions of CPython (pre 3.7).
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_settrace.hpp: pyObject_FastCallDict = reinterpret_cast<_PyObject_FastCallDict*>(&PyObject_FastCallDictCustom);
    PYPI-2023-04-13/pydevd-2.9.5.tar.gz: pydevd-2.9.5/pydevd_attach_to_process/common/py_utils.hpp: typedef PyObject * (_PyObject_FastCallDict)(
    
    PYPI-2023-04-13/ptvsd-4.3.2.zip: ptvsd-4.3.2/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp: DEFINE_PROC(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
    PYPI-2023-04-13/ptvsd-4.3.2.zip: ptvsd-4.3.2/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace_37.hpp: _PyObject_FastCallDict* pyObject_FastCallDict;
    PYPI-2023-04-13/ptvsd-4.3.2.zip: ptvsd-4.3.2/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace_37.hpp: // result = _PyObject_FastCall(callback, stack, 3);
    PYPI-2023-04-13/ptvsd-4.3.2.zip: ptvsd-4.3.2/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace_37.hpp: // Note that _PyObject_FastCall is actually a define:
    PYPI-2023-04-13/ptvsd-4.3.2.zip: ptvsd-4.3.2/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace_37.hpp: // #define _PyObject_FastCall(func, args, nargs) _PyObject_FastCallDict((func), (args), (nargs), NULL)
    PYPI-2023-04-13/ptvsd-4.3.2.zip: ptvsd-4.3.2/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_utils.hpp: typedef PyObject * (_PyObject_FastCallDict)(
    
  9. vstinner commented on Jun 30, 2023

    @vstinner
    vstinner
    on Jun 30, 2023
    MemberAuthor
    More actions

    PyObject_Vectorcall() is available since Python 3.8 and PyObject_VectorcallDict() is available since Python 3.9.

    _PyObject_FastCall() and _PyObject_FastCallDict() are private functions. IMO it's ok to remove them.

    The METH_FASTCALL calling convention remains useful and I don't see a need to remove.

  10. vstinner
    added 2 commits that reference this issue on Jun 30, 2023

    pythongh-106023: Rename _PyObject_FastCallDictTstate()

    783b399

    pythongh-106023: Remove _PyObject_FastCall() function

    4d8af2c
  11. 1 remaining item

  12. encukou commented on Jun 30, 2023

    @encukou
    encukou
    on Jun 30, 2023
    Member
    More actions

    When removing them, it would be nice to add them to What's New, mentioning that they were private but documenting what to call instead.
    When you've inherited code that stops compiling, it's nice to have instructions like that.

  13. vstinner
    added 5 commits that reference this issue on Jun 30, 2023

    gh-106023: Rename _PyObject_FastCallDictTstate() (#106264)

    e17420d

    pythongh-106023: Remove _PyObject_FastCall() function

    66796ca

    gh-106023: Remove _PyObject_FastCall() function (#106265)

    2efdd2a

    pythongh-106023: Remove _PyObject_FastCallTstate()

    b82b6d0

    pythongh-106023: Remove _PyObject_FastCallTstate() function

    dcb1968
  14. vstinner commented on Jun 30, 2023

    @vstinner
    vstinner
    on Jun 30, 2023
    MemberAuthor
    More actions

    I removed the FastCall C API and explained how to migrate in What's New in Python 3.13.

    I removed _PyObject_FastCall(), _PyObject_FastCallDict(), _PY_FASTCALL_SMALL_STACK and _PyStack_AsDict() from the public C API. I removed _PyObject_FastCallTstate() and _PyObject_FastCallDictTstate() of the internal C API.

    The METH_FASTCALL constant stays for now.

  15. vstinner
    closed this as completedon Jun 30, 2023
  16. vstinner
    added a commit that references this issue on Jun 30, 2023

    gh-106023: Remove _PyObject_FastCallTstate() function (#106273)

    80b3d8f
  17. vstinner
    added 3 commits that reference this issue on Apr 8, 2024

    gh-106023: Update What's New in 3.13: _PyObject_FastCall() (#117633)

    9a12f5d

    Revert "pythongh-106023: Update What's New in 3.13: _PyObject_FastCal…

    4d0ded5

    Revert "gh-106023: Update What's New in 3.13: _PyObject_FastCall() (#…

    a25c02e
  18. diegorusso
    added 2 commits that reference this issue on Apr 17, 2024

    pythongh-106023: Update What's New in 3.13: _PyObject_FastCall() (pyt…

    415d08c

    Revert "pythongh-106023: Update What's New in 3.13: _PyObject_FastCal…

    fbc917a
  19. hauntsaninja
    added a commit that references this issue on Jul 7, 2024

    Fix `_PyObject_FastCall` for Python 3.13 (#17502)

    4c54801
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.