重大事件:PHP JIT 已进入 PHP RFC,并将在PHP 8.0中实现.

查看 https://wiki.php.net/rfc/jit

以下为翻译:

PHP RFC:JIT

介绍

众所周知,PHP 7的性能跳跃最初是由尝试为PHP实现JIT而启动的。我们在2011年开始在Zend(主要是由Dmitry)开展这些工作,从那时起尝试了3种不同的实现。我们从未提出过发布其中任何一个的建议,主要有三个原因:它们导致典型的Web应用程序没有实质性的性能提升; 它们的开发和维护非常复杂; 我们仍然有其他方向可以探索以提高性能,而无需使用JIT。

今日JIT的案例

尽管支持JIT的PHP的大部分基础都没有改变 - 我们相信今天有一个很好的案例可用于支持JIT的PHP。

首先,我们相信我们已经达到了使用其他优化策略提高PHP性能的能力。换句话说 - 除非我们使用JIT,否则我们无法进一步提高PHP的性能。

其次 - 使用JIT可能会为在其他非Web,CPU密集型场景中更频繁使用PHP打开大门 - 其中性能优势实际上非常可观 - 而且今天可能还没有考虑PHP。

最后 - 使JIT可用可以为我们(额外的努力)提供在PHP中开发内置函数的能力,而不是(或除了)C - 而不会遭受与此类策略相关的巨大性能损失。今天的非JITted发动机。反过来,这可以为更快的创新打开大门 - 而且还可以实现更安全的实施,这将不太容易受到内存管理,溢出和与基于C的开发相关的类似问题的影响。

提案

我们建议将JIT包含在PHP 8中,并提供额外的工作来提高其性能和可用性。

此外,我们建议考虑在PHP 7.4中包含JIT作为实验性功能(默认情况下禁用)。

PHP JIT实现为OPcache的几乎独立部分。它可以在PHP编译时和运行时启用/禁用。启用后,PHP文件的本机代码存储在OPcache共享内存的另一个区域中,op_array→o​​pcodes []。处理程序保留指向JIT编码的指针。这种方法根本不需要引擎修改。

我们使用DynAsm(为LuaJIT项目开发)来生成本机代码。它是一个非常轻量级和高级的工具,但它确实承担了目标汇编语言的良好和非常低级的开发知识。在过去,我们尝试过LLVM,但它的代码生成速度几乎慢了100倍,因此使用起来非常昂贵。目前,我们仅在POSIX平台上支持x86和x86_64。Windows支持应该相对简单,但对我们来说是(并且仍然是)低优先级。DynAsm还支持ARM。ARM64,MIPS,MIPS64和PPC,理论上我们应该能够支持PHP部署中常用的所有平台(给予足够的努力)。

JIT的质量可以在https://gist.github.com/dstogov/12323ad13d3240aee8f1上发布的Mandelbrot基准测试中得到证明,它可以将性能提高4倍以上(0.011秒对PHP 7.4的0.046秒)。

function iterate($x,$y)
    {
        $cr = $y-0.5;
        $ci = $x;
        $zr = 0.0;
        $zi = 0.0;
        $i = 0;
        while (true) {
            $i++;
            $temp = $zr * $zi;
            $zr2 = $zr * $zr;
            $zi2 = $zi * $zi;
            $zr = $zr2 - $zi2 + $cr;
            $zi = $temp + $temp + $ci;
            if ($zi2 + $zr2 > BAILOUT)
                return $i;
            if ($i > MAX_ITERATIONS)
                return 0;
        }
 
    }

以下是为上面的PHP函数生成的完整汇编代码,主循环代码在.L5和.L7之间可见:

JIT$Mandelbrot::iterate: ; (/home/dmitry/php/bench/b.php)
  sub $0x10, %esp
  cmp $0x1, 0x1c(%esi)
  jb .L14
  jmp .L1
.ENTRY1:
  sub $0x10, %esp
.L1:
  cmp $0x2, 0x1c(%esi)
  jb .L15
  mov $0xec3800f0, %edi
  jmp .L2
.ENTRY2:
  sub $0x10, %esp
.L2:
  cmp $0x5, 0x48(%esi)
  jnz .L16
  vmovsd 0x40(%esi), %xmm1
  vsubsd 0xec380068, %xmm1, %xmm1
.L3:
  mov 0x30(%esi), %eax
  mov 0x34(%esi), %edx
  mov %eax, 0x60(%esi)
  mov %edx, 0x64(%esi)
  mov 0x38(%esi), %edx
  mov %edx, 0x68(%esi)
  test $0x1, %dh
  jz .L4
  add $0x1, (%eax)
.L4:
  vxorps %xmm2, %xmm2, %xmm2
  vxorps %xmm3, %xmm3, %xmm3
  xor %edx, %edx
.L5:
  cmp $0x0, EG(vm_interrupt)
  jnz .L18
  add $0x1, %edx
  vmulsd %xmm3, %xmm2, %xmm4
  vmulsd %xmm2, %xmm2, %xmm5
  vmulsd %xmm3, %xmm3, %xmm6
  vsubsd %xmm6, %xmm5, %xmm7
  vaddsd %xmm7, %xmm1, %xmm2
  vaddsd %xmm4, %xmm4, %xmm4
  cmp $0x5, 0x68(%esi)
  jnz .L19
  vaddsd 0x60(%esi), %xmm4, %xmm3
.L6:
  vaddsd %xmm5, %xmm6, %xmm6
  vucomisd 0xec3800a8, %xmm6
  jp .L13
  jbe .L13
  mov 0x8(%esi), %ecx
  test %ecx, %ecx
  jz .L7
  mov %edx, (%ecx)
  mov $0x4, 0x8(%ecx)
.L7:
  test $0x1, 0x39(%esi)
  jnz .L21
.L8:
  test $0x1, 0x49(%esi)
  jnz .L23
.L9:
  test $0x1, 0x69(%esi)
  jnz .L25
.L10:
  movzx 0x1a(%esi), %ecx
  test $0x496, %ecx
  jnz JIT$$leave_function
  mov 0x20(%esi), %eax
  mov %eax, EG(current_execute_data)
  test $0x40, %ecx
  jz .L12
  mov 0x10(%esi), %eax
  sub $0x1, (%eax)
  jnz .L11
  mov %eax, %ecx
  call zend_objects_store_del
  jmp .L12
.L11:
  mov 0x4(%eax), %ecx
  and $0xfffffc10, %ecx
  cmp $0x10, %ecx
  jnz .L12
  mov %eax, %ecx
  call gc_possible_root
.L12:
  mov %esi, EG(vm_stack_top)
  mov 0x20(%esi), %esi
  cmp $0x0, EG(exception)
  mov (%esi), %edi
  jnz JIT$$leave_throw
  add $0x1c, %edi
  add $0x10, %esp
  jmp (%edi)
.L13:
  cmp $0x3e8, %edx
  jle .L5
  mov 0x8(%esi), %ecx
  test %ecx, %ecx
  jz .L7
  mov $0x0, (%ecx)
  mov $0x4, 0x8(%ecx)
  jmp .L7
.L14:
  mov %edi, (%esi)
  mov %esi, %ecx
  call zend_missing_arg_error
  jmp JIT$$exception_handler
.L15:
  mov %edi, (%esi)
  mov %esi, %ecx
  call zend_missing_arg_error
  jmp JIT$$exception_handler
.L16:
  cmp $0x4, 0x48(%esi)
  jnz .L17
  vcvtsi2sd 0x40(%esi), %xmm1, %xmm1
  vsubsd 0xec380068, %xmm1, %xmm1
  jmp .L3
.L17:
  mov %edi, (%esi)
  lea 0x50(%esi), %ecx
  lea 0x40(%esi), %edx
  sub $0xc, %esp
  push $0xec380068
  call sub_function
  add $0xc, %esp
  cmp $0x0, EG(exception)
  jnz JIT$$exception_handler
  vmovsd 0x50(%esi), %xmm1
  jmp .L3
.L18:
  mov $0xec38017c, %edi
  jmp JIT$$interrupt_handler
.L19:
  cmp $0x4, 0x68(%esi)
  jnz .L20
  vcvtsi2sd 0x60(%esi), %xmm3, %xmm3
  vaddsd %xmm4, %xmm3, %xmm3
  jmp .L6
.L20:
  mov $0xec380240, (%esi)
  lea 0x80(%esi), %ecx
  vmovsd %xmm4, 0xe0(%esi)
  mov $0x5, 0xe8(%esi)
  lea 0xe0(%esi), %edx
  sub $0xc, %esp
  lea 0x60(%esi), %eax
  push %eax
  call add_function
  add $0xc, %esp
  cmp $0x0, EG(exception)
  jnz JIT$$exception_handler
  vmovsd 0x80(%esi), %xmm3
  jmp .L6
.L21:
  mov 0x30(%esi), %ecx
  sub $0x1, (%ecx)
  jnz .L22
  mov $0x1, 0x38(%esi)
  mov $0xec3802b0, (%esi)
  call rc_dtor_func
  jmp .L8
.L22:
  mov 0x4(%ecx), %eax
  and $0xfffffc10, %eax
  cmp $0x10, %eax
  jnz .L8
  call gc_possible_root
  jmp .L8
.L23:
  mov 0x40(%esi), %ecx
  sub $0x1, (%ecx)
  jnz .L24
  mov $0x1, 0x48(%esi)
  mov $0xec3802b0, (%esi)
  call rc_dtor_func
  jmp .L9
.L24:
  mov 0x4(%ecx), %eax
  and $0xfffffc10, %eax
  cmp $0x10, %eax
  jnz .L9
  call gc_possible_root
  jmp .L9
.L25:
  mov 0x60(%esi), %ecx
  sub $0x1, (%ecx)
  jnz .L26
  mov $0x1, 0x68(%esi)
  mov $0xec3802b0, (%esi)
  call rc_dtor_func
  jmp .L10
.L26:
  mov 0x4(%ecx), %eax
  and $0xfffffc10, %eax
  cmp $0x10, %eax
  jnz .L10
  call gc_possible_root
  jmp .L10

向后不兼容的变化

没有

建议的PHP版本

PHP 8和PHP 7.4(单独投票)

RFC影响

致SAPI

没有

到现有扩展

没有

对Opcache

JIT将作为OPcache的一部分实现。

新常数

没有

php.ini默认值

如果有任何php.ini设置,则列出:

  • opcache.jit_buffer_size - 为本机代码生成保留的共享内存缓冲区的大小(以兆字节为单位)。默认值 - 0禁用JIT。
  • opcache.jit - JIT控件选项。由4位十进制数字组成 - CRTO(默认为1205.可能最好更改为1235)。
    • O - 优化级别
      • 0 - 不要JIT
      • 1 - 最小JIT(调用标准VM处理程序)
      • 2 - 选择性VM处理程序内联
      • 3 - 基于单个函数的静态类型推断的优化JIT
      • 4 - 基于静态类型推断和调用树的优化JIT
      • 5 - 基于静态类型推断和内部过程分析的优化JIT
    • T - JIT触发器
      • 0 - JIT在第一个脚本加载时的所有函数
      • 1 - 首次执行时的JIT功能
      • 2 - 第一次请求时的配置文件,第二次请求时编译热门功能
      • 3 - 动态配置文件并编译热门功能
      • 4 - 在doc-comments中使用@jit标记编译函数
    • R - 寄存器分配
      • 0 - 不执行寄存器分配
      • 1 - 使用本地线性扫描寄存器分配器
      • 2 - 使用全局线性扫描寄存器分配器
    • C - CPU特定的优化标志
      • 0 - 没有
      • 1 - 启用AVX指令生成
  • opcache.jit_debug - JIT调试控制选项,其中每个位启用一些调试选项。默认 - 0。
    • (1«0) - 打印生成的汇编程序
    • (1«1) - 用于代码生成的打印中间SSA表单
    • (1«2) - 注册分配信息
    • (1«4) - 允许使用GDB调试JIT编码
    • (1«5) - 生成perf.map文件以列出Linux perf报告中的JIt-ed函数
    • (1«6) - 提供有关Linux Oprofile的JIt-ed代码的信息
    • (1«7) - 提供有关英特尔VTune的JIt-ed代码的信息
    • (1«8) - 生成perf.dump文件以在Linux perf peport中显示JIT-ed函数的汇编代码

性能

JIT使bench.php的速度提高了两倍多:0.140秒vs 0.320秒。预计这将使大多数CPU密集型工作负载运行得更快。然而,就像以前的尝试一样 - 它目前似乎没有显着改善像WordPress这样的现实应用程序(opcache.jit = 1235 326 req / sec vs 315 req / sec)。

开放式问题

投票开始时确保没有未解决的问题!

未来范围

在PHP 8中,我们将在热函数的初始分析之后改进JIT并执行优化的代码生成。这将允许应用推测性优化并仅生成真正执行的代码。也可以将JIT与预加载和FFI进行更深层次的集成,也许是开发(和提供)用PHP编写的内置函数的标准化方法,而不仅仅是在C语言中。

提议的投票选择

该项目需要50%+ 1的多数。

将JIT包含在PHP 8中?
真正的名字 没有
最后结果: 0 0
此民意调查已经结束。

由于PHP 7.4已经分支并且其引擎预计不会发生显着变化(因此需要对JIT实现进行相应更改),我们还可以考虑在PHP-7.4中包含JIT作为实验性功能(默认情况下禁用)。

将JIT包含在PHP 7.4中(实验性的)?
真正的名字 没有
最后结果: 0 0
此民意调查已经结束。

补丁和测试

https://github.com/zendtech/php-src/ - PHP JIT分支是在两年多前宣布的,从那时起就与PHP master保持一致。

履行

项目实施后,此部分应包含

  1. 它被合并的版本
  2. git commit(s)的链接
  3. 该功能的PHP手册条目的链接
  4. 指向语言规范部分的链接(如果有)

参考

被拒绝的功能

使用邮件列表中讨论的功能更新此更新。

RFC / jit.txt ·最后修改时间:2019/01/31 09:31 by 梅德
文章来自Lenix的博客,转载请注明来源,地址 http://blog.p2hp.com/archives/5642

最后更新于 2019年4月17日

重大事件:PHP JIT 已进入 PHP RFC,并将在PHP 8.0中实现
标签: