python / python/cpython

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

オープン
#150,712 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

3.16 interpreter-core performance topic-JIT
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

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();
            }
        }
   }

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

bytecodes.c と issue 143158 の hot-cold splitting に関する議論から始め、uop traces がどのように lower され、tooling が exits をどのように表現するかを追跡します。exit ops と custom exits が挿入された _EXIT_TRACE targets をどのように置き換えるかを定義し、side-exit の補償コードを意図どおりに表現して sink できることを確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
c, python
領域
compilers, performance
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
静か
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。