V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
yang623601
V2EX  ›  程序员

PythonJIT-如何执行

  •  
  •   yang623601 · 10 小时 16 分钟前 · 251 次点击

    前面介绍那么多只是为了做一些铺垫,每个操作码对应一个 emit 函数。什么时候调用这些函数就是我们接下来需要研究的。

    回顾

    // Compile the shim, which handles converting between the native
    // calling convention and the calling convention used by jitted code
    // (which may be different for efficiency reasons).

    JIT 代码转换时机

    stencil_groups 调用

    在项目代码内搜索 stencil_groups 可以看到它在函数 _PyJIT_Compile 被索引,index 为操作码。往下继续看代码,stencil_groups 在两个循环里被索引。第一个循环设置代码块的开始 state.instruction_starts[i] = code_size 位置;而第二个循环是调用 OpCode 对应的 emit 函数,将调用转化为我们的 jit 代码。完成内存转换后,将这块内存标记为可执行,调用 c 函数 mprotect,标记失败返回 -1 ,释放内存。

    static int
    mark_executable(unsigned char *memory, size_t size)
    {
        if (size == 0) {
            return 0;
        }
        assert(size % get_page_size() == 0);
        // Do NOT ever leave the memory writable! Also, don't forget to flush the
        // i-cache (I cannot begin to tell you how horrible that is to debug):
    #ifdef MS_WINDOWS
        if (!FlushInstructionCache(GetCurrentProcess(), memory, size)) {
            jit_error("unable to flush instruction cache");
            return -1;
        }
        int old;
        int failed = !VirtualProtect(memory, size, PAGE_EXECUTE_READ, &old);
    #else
        int failed = 0;
        __builtin___clear_cache((char *)memory, (char *)memory + size);
    #ifndef MAP_JIT
        failed = mprotect(memory, size, PROT_EXEC | PROT_READ);
    #endif
    #endif
        if (failed) {
            jit_error("unable to protect executable memory");
            return -1;
        }
        return 0;
    }
    

    用 uops 缓冲区创建执行器

    通过微指令创建 executor

    JIT 是 PythonJIT 的核心,通过 JIT 生成的代码,我们可以看到它是通过 uops 缓冲区来执行的。这个缓冲区是一个很小的内存区域,用来存储 CPU 的微指令。在 JIT 代码生成后,我们需要将这些代码放入 uops 缓冲区,然后执行。 可以理解为一个如下函数,传入 JIT 生成的代码,然后执行。

    static void
    execute_jit_code(unsigned char *memory, size_t size)
    {
        void (*fn)(void) = (void (*)(void))memory;
        fn();
    }
    

    何时开启 JIT 优化

    开启 JIT 优化

    是否开启 jit 有一部分有预处理定义控制(即 _Py_JIT 定义开启),另一部分就是 config->perf_profiling == 0,否则会抛出异常。_PyOptimizer_NewUOpOptimizer 将 uop_optimize 函数赋值给了 opt->optimize ,这个函数是在 Python/optimizer.c 中定义的。JIT 的核心优化逻辑就在 uop_optimize 函数中。

    PyObject *
    _PyOptimizer_NewUOpOptimizer(void)
    {
        _PyOptimizerObject *opt = PyObject_New(_PyOptimizerObject, &_PyUOpOptimizer_Type);
        if (opt == NULL) {
            return NULL;
        }
        opt->optimize = uop_optimize;
        return (PyObject *)opt;
    }
    

    比如,translate_bytecode_to_trace 返回的长度为 0 ;环境变量 PYTHON_UOPS_OPTIMIZE 设置为 0 等都不会开启 JIT 优化。uop_optimize 完成 _PyExecutorObject **exec_ptr 的替换,其函数的定义如下:

    static int
    uop_optimize(
        _PyOptimizerObject *self,
        _PyInterpreterFrame *frame,
        _Py_CODEUNIT *instr,
        _PyExecutorObject **exec_ptr,
        int curr_stackentries,
        bool progress_needed)
    {
        ...
    }
    

    JIT 优化算法的核心逻辑可以在 uop_optimize 函数中找到,其最终的目的就是决定是否使用 JIT 优化。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2644 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 18ms · UTC 00:33 · PVG 08:33 · LAX 16:33 · JFK 19:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.