python / python/cpython

Hot-cold splitting in the cases generator and JIT optimizer.

Open
#150,712 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

3.16 interpreter-core performance topic-JIT
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

https://github.com/python/cpython/issues/143158 describes hot-cold splitting in the JIT, but that is too late for optimizations that sink code onto exits.

To support those optimizations, we need to split uops as defined into bytecodes.c into "hot" and "cold" ops.

From https://github.com/python/cpython/issues/143158#issuecomment-4544178632

For example, we want to transform this:

        op (_GUARD_IS_NONE_POP, (val -- )) {
            int is_none = PyStackRef_IsNone(val);
            if (!is_none) {
                PyStackRef_CLOSE(val);
                AT_END_EXIT_IF(1);
            }
            DEAD(val);
        }

into this:

        op (_GUARD_IS_NONE_POP, (val -- )) {
            int is_none = PyStackRef_IsNone(val);
            if (!is_none) {
                goto _GUARD_IS_NONE_POP_COLD; // tail call
            }
            DEAD(val);
        }

        op (_GUARD_IS_NONE_POP_COLD, (val -- )) {
            PyStackRef_CLOSE(val);
            AT_END_EXIT_IF(1);
        }

Currently, during lowering of the uop trace to a form suitable for execution we need to insert an _EXIT_TRACE uop as the target for exit jumps in the uop. We want to replace those with the "cold" uop followed by the exit, allowing compensation code to be more easily sunk onto side exits.

While it is appealing to do this automatically, it might make more sense to do the splitting manually, and add support for custom exits to the tooling.

We could mark ops as exit and explicitly state which exit is being used (with _EXIT_TRACE as the default exit).
So _GUARD_IS_NONE_POP would be rewritten as:

        op (_GUARD_IS_NONE_POP, (val -- )) {
            int is_none = PyStackRef_IsNone(val);
            if (!is_none) {
                EXIT_TO(POP_TOP_EXIT);
            }
            DEAD(val);
        }

        exit op (POP_TOP_EXIT, (val -- )) {
            PyStackRef_CLOSE(val);
        }

Likewise, _ITER_NEXT_INLINE would be rewritten as:

    tier2 op(_ITER_NEXT_INLINE, (iternext_fn/4, iter, null_or_index -- iter, null_or_index, next)) {
        assert(sizeof(iternextfunc) == sizeof(uintptr_t));
        volatile iternextfunc iternext_v = (iternextfunc)iternext_fn;
        PyObject *item = iternext_v(PyStackRef_AsPyObjectBorrow(iter));
        if (item == NULL) {
            EXIT_TO(_ITER_NEXT_INLINE_EXIT);
        }
        STAT_INC(FOR_ITER, hit);
        next = PyStackRef_FromPyObjectSteal(item);
    }

    exit op(_ITER_NEXT_INLINE_EXIT, ( -- )) {
        if (_PyErr_Occurred(tstate)) {
            if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
                _PyEval_MonitorRaise(tstate, frame, frame->instr_ptr);
                _PyErr_Clear(tstate);
            }
            else {
                ERROR_NO_POP();
            }
        }
   }

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with bytecodes.c and the hot-cold splitting discussion in issue 143158, then trace how uop traces are lowered and how the tooling represents exits. Define how exit ops and custom exits replace inserted _EXIT_TRACE targets, and verify that side-exit compensation code can be represented and sunk as intended.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, python
Domain
compilers, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.