如何加速PHP数学计算?

3
我尝试将来自这里的短代码从JavaScript直接转换为PHP进行基准测试。
Javascript:
(function() {
    var a = 3.1415926, b = 2.718;
    var i, j, d1, d2;
    for(j=0; j<10; j++) {
        for(i=0; i<100000000; i++) {
            a = a + b;
        }
    }
    console.log("a = " + a);
})();

PHP:

<?php

$a = 3.1415926;
$b = 2.718;

for($j=0; $j<10; $j++) {
    for($i=0; $i<100000000; $i++) {
        $a = $a + $b;
    }
}
echo "a = $a\n";

在控制台(我的Macbook终端,node v5.7.0 vs PHP 7.0.4)上运行,结果相当令人困惑:
$ time node test.js
a = 2717999973.76071

real    0m1.340s
user    0m0.912s
sys 0m0.021s

$ time php t.php
a = 2717999973.7607

real    2m40.239s
user    2m35.271s
sys 0m0.507s

“PHP在基本数学运算中真的比Node慢120倍吗?有什么可以优化的方法吗?”

3
记住代码运行的位置很重要。使用PHP时,资源是从您的托管服务器中提取的。服务器上慢的CPU和慢的内存会导致PHP代码运行缓慢。JavaScript中的数学计算是在用户的计算机上通过浏览器解释器运行的。JavaScript中的数学计算依赖于用户计算机的速度。 - jjonesdesign
3
如问题所述,我正在我的 MacBook 终端上同时运行它们。它们都是作为 shell 脚本执行的,而不是在客户端浏览器上或远程主机上的服务器上执行。 - pistacchio
1
准确地对PHP/Javascript进行基准测试本身就是一项相当艰巨的任务,我建议您首先展示一下您是如何得出这些数字的。 - RiggsFolly
4
我猜测该节点正在对大部分数学计算进行预编译和优化,将其转换为更少的数学表达式。这是PHP所做不到的。V8引擎高度优化,寻找这样的捷径,而PHP会逐行运行每一行代码。 - Jonathan Kuhn
2
我认为@JonathanKuhn是正确的,Javascript已经发现它可以将整个循环转换为a = a + b * 1000000000; - Barmar
显示剩余2条评论
2个回答

2

虽然我不能解释JavaScript和PHP之间的巨大差异,但我已经能够通过一个小改变使PHP代码的处理速度提高约30%。

在本地运行了几个测试后,我发现更改

$a = $a + $b;

to

$a += $b;

在我的电脑上(i5 8GB RAM和较慢的磁盘),处理时间从78-80秒降至53-55秒。使用类似的节省估算(向下取整到25%)应该将总处理时间缩短到约2分钟。显然,这与JavaScript时间相比还有差距。
根据实际需求(因为基准测试只能告诉你很少的信息),可能还有其他改进方法,但对于如此大的数字并非总是适用。

2
JavaScript 代码几乎是 1:1 编译的,因此没有太多实质内容的 10^9 次循环会以 CPU 允许的速度运行(在这里:使用旧版 Node v0.10.25 运行时间为 2.14 秒)。
另一方面,PHP 做了很多工作,特别是 Zend 引擎。如果使用 VLD 转储你的小程序的操作码,你会得到以下结果(php 7.0.5):
$ php -d vld.active=1 -d vld.execute=0   -f benchmark.php

Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 14
Branch analysis from position: 14
Jump found. Position 1 = 16, Position 2 = 4
Branch analysis from position: 16
Jump found. Position 1 = -2
Branch analysis from position: 4
Jump found. Position 1 = 10
Branch analysis from position: 10
Jump found. Position 1 = 12, Position 2 = 6
Branch analysis from position: 12
Jump found. Position 1 = 16, Position 2 = 4
Branch analysis from position: 16
Branch analysis from position: 4
Branch analysis from position: 6
Jump found. Position 1 = 12, Position 2 = 6
Branch analysis from position: 12
Branch analysis from position: 6
filename:       benchmark.php
function name:  (null)
number of ops:  21
compiled vars:  !0 = $a, !1 = $b, !2 = $j, !3 = $i
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   ASSIGN                                                   !0, 3.14159
   3     1        ASSIGN                                                   !1, 2.718
   5     2        ASSIGN                                                   !2, 0
         3      > JMP                                                      ->14
   6     4    >   ASSIGN                                                   !3, 0
         5      > JMP                                                      ->10
   7     6    >   ADD                                              ~8      !0, !1
         7        ASSIGN                                                   !0, ~8
   6     8        POST_INC                                         ~10     !3
         9        FREE                                                     ~10
        10    >   IS_SMALLER                                       ~11     !3, 100000000
        11      > JMPNZ                                                    ~11, ->6
   5    12    >   POST_INC                                         ~12     !2
        13        FREE                                                     ~12
        14    >   IS_SMALLER                                       ~13     !2, 10
        15      > JMPNZ                                                    ~13, ->4
  10    16    >   ROPE_INIT                                     3  ~15     'a+%3D+'
        17        ROPE_ADD                                      1  ~15     ~15, !0
        18        ROPE_END                                      2  ~14     ~15, '%0A'
        19        ECHO                                                     ~14
        20      > RETURN                                                   1

branch: #  0; line:     2-    5; sop:     0; eop:     3; out1:  14
branch: #  4; line:     6-    6; sop:     4; eop:     5; out1:  10
branch: #  6; line:     7-    6; sop:     6; eop:     9; out1:  10
branch: # 10; line:     6-    6; sop:    10; eop:    11; out1:  12; out2:   6
branch: # 12; line:     5-    5; sop:    12; eop:    13; out1:  14
branch: # 14; line:     5-    5; sop:    14; eop:    15; out1:  16; out2:   4
branch: # 16; line:    10-   10; sop:    16; eop:    20; out1:  -2
path #1: 0, 14, 16, 
path #2: 0, 14, 4, 10, 12, 14, 16, 
path #3: 0, 14, 4, 10, 6, 10, 12, 14, 16, 

你两个版本的区别是:
1,10c1,10
< branch: #  0; line:     2-    5; sop:     0; eop:     3; out1:  14
< branch: #  4; line:     6-    6; sop:     4; eop:     5; out1:  10
< branch: #  6; line:     7-    6; sop:     6; eop:     9; out1:  10
< branch: # 10; line:     6-    6; sop:    10; eop:    11; out1:  12; out2:   6
< branch: # 12; line:     5-    5; sop:    12; eop:    13; out1:  14
< branch: # 14; line:     5-    5; sop:    14; eop:    15; out1:  16; out2:   4
< branch: # 16; line:    10-   10; sop:    16; eop:    20; out1:  -2
< path #1: 0, 14, 16, 
< path #2: 0, 14, 4, 10, 12, 14, 16, 
< path #3: 0, 14, 4, 10, 6, 10, 12, 14, 16, 
---
> branch: #  0; line:     2-    5; sop:     0; eop:     3; out1:  13
> branch: #  4; line:     6-    6; sop:     4; eop:     5; out1:   9
> branch: #  6; line:     7-    6; sop:     6; eop:     8; out1:   9
> branch: #  9; line:     6-    6; sop:     9; eop:    10; out1:  11; out2:   6
> branch: # 11; line:     5-    5; sop:    11; eop:    12; out1:  13
> branch: # 13; line:     5-    5; sop:    13; eop:    14; out1:  15; out2:   4
> branch: # 15; line:    10-   10; sop:    15; eop:    19; out1:  -2
> path #1: 0, 13, 15, 
> path #2: 0, 13, 4, 9, 11, 13, 15, 
> path #3: 0, 13, 4, 9, 6, 9, 11, 13, 15, 

它(`$a += $b`)只是使用了不同的,稍微慢一点的路径。是的,慢:我的测试结果为 `$a = $a + $b` 花费了 17 秒,而 `$a += $b` 则花费了 20 秒。虽然差别不大,但是很显著。和 JavaScript 相比也没有太大的区别。
对于您所使用的 PHP 版本来说,两分钟的时间相当长,即使是旧的 PHP-5 在这里也只需要 40 秒钟。我在变更日志中找不到任何东西,但如果可能的话,您可以尝试更新版本。或者,如果您自己编译,可以尝试不同的优化,因为 MAC 仍然与“普通” PC 不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接