TGViewer
Находки в опенсорсе Находки в опенсорсе @opensource_findings · 12.6K subscribers
Post #872 12.6K
Как работает диспатчеризация байткода внутри VM? Computed GOTOs

Многие из вас знают, что внутри питона есть большой switch-case, который выполняется в цикле, он находит нужный байткод и выполняет его. Выглядит оно примерно как-то так:


#define LOAD_CONST 79

PyObject* _Py_HOT_FUNCTION
_PyEval_EvalFrameDefault(PyThreadState *tstate,
_PyInterpreterFrame *frame,
int throwflag)
{
DISPATCH_GOTO(); // разворачивается в `goto dispatch_opcode`
dispatch_opcode:
switch (opcode) {
TARGET(LOAD_CONST): // разворачивается в `case 79:`
{
frame->instr_ptr = next_instr;
next_instr += 1;
_PyStackRef value = PyStackRef_FromPyObjectNew(
GETITEM(FRAME_CO_CONSTS, oparg));
// ...
}
// ...
}

opcode = next_instr->op.code;
DISPATCH_GOTO(); // разворачивается в `goto dispatch_opcode;`

exit:
// end of cycle: success or error
}


Но, на самом деле – все не совсем так просто! Данный switch по сути является самой горячей частью кода во всем интерпретаторе, он выполняется буквально на любое действие. Любое ускорение данного места дает ускорение всему коду на питоне. А значит – такие ускорения были придуманы.

Концепт Computed GOTOs

Вводная статья на тему, кто вообще никогда о таком не слышал. Если очень кратко:
- Создаем известную в compile-time таблицу переходов, которая использует лейблы для goto. Назовем ее opcode_targets
- Вместо switch просто используем goto *opcode_targets[opcode]
- Проверяем в configure, что компилятор поддерживает такую фичу (`gcc` поддерживает, --with-computed-gotos по-умолчанию включено)
- Накручиваем DSL для виртуальной машины:


#if USE_COMPUTED_GOTOS
# define TARGET(op) TARGET_##op:
# define DISPATCH_GOTO() goto *opcode_targets[opcode]
#else
# define TARGET(op) case op: TARGET_##op:
# define DISPATCH_GOTO() goto dispatch_opcode
#endif


Итого, используя тот же DSL на макросах, благодаря флагу USE_COMPUTED_GOTOS (который выставляется в configure) – получаем совсем другой код в _PyEval_EvalFrameDefault:


#define LOAD_CONST 79

PyObject* _Py_HOT_FUNCTION
_PyEval_EvalFrameDefault(PyThreadState *tstate,
_PyInterpreterFrame *frame,
int throwflag)
{
DISPATCH_GOTO(); // // goto *opcode_targets[opcode]
TARGET(LOAD_CONST): // TARGET_79:
{
frame->instr_ptr = next_instr;
next_instr += 1;
_PyStackRef value = PyStackRef_FromPyObjectNew(
GETITEM(FRAME_CO_CONSTS, oparg));
// ...
}
// ...

opcode = next_instr->op.code;
DISPATCH_GOTO(); // goto *opcode_targets[opcode]

exit:
// end of cycle: success or error
}


Данная реализация где-то на 15% быстрее реализации на switch. Но для простоты все продолжают говорить, что внутри VM switch+case

Узнали сегодня что-то новое? :)

| Поддержать | YouTube | GitHub |
  • 🔥 96
  • 👍 36
  • ❤ 18
  • 🤯 2
  • 😱 1
More from @opensource_findings
  1. Sep 28, 2026Еще анонсы докладов на бесплатную конференцию 17 октября в НН Регистрация: https://itgorky…
  2. Sep 25, 2026Монадические выражения в Python. Наконец-то! Продолжаем разговор про интересные PEPы. Пого…
  3. Sep 15, 2026PEPы в Python окончательно вышли из-под контроля Давайте посмотрим, что происходит с ПЕПам…
  4. Sep 10, 2026Большая бесплатная конференция в Нижнем Новгороде 17 октября Регистрация: https://itgorky.…
  5. Sep 1, 2026Post #985
  6. Aug 24, 2026JIT могут удалить из CPython! PEP-836: https://peps.python.org/pep-0836 JIT в CPython имее…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →