为什么在执行这个简单的循环时,PHP7比Python3快得多?

14
作为一个非常简单的基准测试,我在同一台树莓派3型号B上执行了以下简单的PHP 7.0.19-1和Python 3.5.3(命令行)代码。
相比之下,Python的执行时间非常慢(74秒对比1.4秒的PHP)。有人能帮我理解为什么Python的执行时间要比PHP长这么多吗?我做错了什么,或者有一些优化/设置可以提高其性能以满足或超过PHP的性能吗?还是Python就是那么慢(肯定不是!)?
我看到了这个基准测试,报告说PHP 7在其他语言中表现出色,但是当进行这样一个简单操作时,你会认为两者都会被优化得差不多。
如果将字符串赋值替换为加法,Python执行循环的速度将快两倍。但即使如此,仍然需要34秒,而PHP只需要约1.1秒。
PHP7代码:
<?php

function test($x)
{
    $t1 = microtime(true);
    $a = 0;
    for($i = 0; $i < $x; $i++)
    {
        $a++;
    }
    $t2 = microtime(true);

    echo "Time for $x was " . ($t2 - $t1) . "\n";

    return $a;
}


echo test(100000);
echo test(1000000);
echo test(10000000);

结果: 100000的时间为0.036377191543579 1000000的时间为0.18501400947571 10000000的时间为1.3939099311829

Python3代码:

import time
def test(x):
    t1 = time.clock()
    a = 0
    for i in range(x):
        a += 1
    t2 = time.clock()
    print("Time for {} was {}".format(x, t2 - t1))
    return x

print(test(1000000))
print(test(10000000))
print(test(100000000))

结果: 1000000个项目的时间为0.761641 1000000 10000000个项目的时间为7.427618000000001 10000000 100000000个项目的时间为74.320387

更新:是的,当@Amber指出后,我意识到循环计数器相差一个数量级。尽管如此,答案真的很有趣,所以问这个问题还是值得的。


微基准测试往往会受到噪声和奇怪现象的影响,例如,我在一个随机的虚拟机上运行相同的Python代码只需要几秒钟。如果没有一致的环境和可重复的结果,要明确回答您的问题将会很困难。 - Amber
这就是为什么我在完全相同的硬件、操作系统等上运行了两个测试,我假设这样可以消除硬件不一致性,重点关注解释器速度。 - Ryan Griggs
是的,但我们回答者无法访问那个环境,因此我们不知道可能影响它的因素。我们的硬件不同于你们的;我们的系统环境也不同于你们的。 - Amber
4
请注意,你所拥有的PHP代码中的数字比Python代码中的数字少一个零。你PHP代码中最大的数字是10000000(7个零),而Python中最大的数字是100000000(8个零)。 - Amber
2
我只有一个反应:哈哈哈哈哈哈 - Amir Hassan Azimi
显示剩余3条评论
5个回答

14

当您使用相同的循环计数而不是 Python 计数以一个数量级更大时,它们的结果非常接近:

PHP: https://ideone.com/3ebkai 2.7089s

<?php

function test($x)
{
    $t1 = microtime(true);
    $a = 0;
    for($i = 0; $i < $x; $i++)
    {
        $a++;
    }
    $t2 = microtime(true);

    echo "Time for $x was " . ($t2 - $t1) . "\n";

    return $a;
}


echo test(100000000);

Python: https://ideone.com/pRFVfk 4.5708秒

import time
def test(x):
    t1 = time.clock()
    a = 0
    for i in range(x):
        a += 1
    t2 = time.clock()
    print("Time for {} was {}".format(x, t2 - t1))
    return x

print(test(100000000))

我是个彻头彻尾的白痴。你说得对,我数了错的零的数量,然后就假定Python非常低效。PEBKAC!感谢你指出这一点。现在我可以回去做一些有用的事情了! :) - Ryan Griggs
PHP可以支持数字文字中的下划线,就像Python 3.6一样,但是我更喜欢使用科学计数法:在Python中使用1e8代替int(1e8) - Cees Timmerman

8

你们不公平。这两段代码并没有做相同的事情。

虽然PHP只增加了两个变量($a和$i),但Python在循环之前生成一个范围。

因此,为了进行公平比较,你的Python代码应该是:

import time
def test2(x):
    r = range(x) #please generate this first
    a = 0

    #now you count only the loop time
    t1 = time.clock()
    for i in r:
        a += 1
    t2 = time.clock()

    print("Time for {} was {}".format(x, t2 - t1))
    return a

还有,它要快得多:

>>> print(test(100000000))
Time for 100000000 was 6.214772

VS

>>> print(test2(100000000))
Time for 100000000 was 3.079545

2
循环本身在CPython 3中似乎慢了两倍: https://ideone.com/bI6jzD
<?php
function test($x)
{
    $t1 = microtime(true);
    $a = 0;
    for($i = 0; $i < $x; ++$i)
    {
        //1.40s Reassign and use $a.
        //$a += 1;
        //1.15s Use and increment $a.
        //$a++;
        //0.88s Increment and use $a.
        //++$a;
        //0.69s Do nothing.
    }
    $t2 = microtime(true);
    echo "Time for $x was " . ($t2 - $t1) . "\n";
    return $a;
}
echo test(1e8);

https://ideone.com/l35EBc

import time

def test(x):
    t1 = time.clock()
    #>5s
    #from functools import reduce
    #a = reduce(lambda a, i: a + i, (1 for i in range(x)), 0)
    a = 0
    for i in range(x):
        #4.38s
        #a += 1
        #1.89s
        pass
    t2 = time.clock()
    print("Time for {} was {}".format(x, t2 - t1))
    return x

print(test(int(1e8)))

然而,这只是Python的标准实现,更注重易于理解而非速度。例如,PyPy3.5 v6.0.0 在我的笔记本上运行该空循环只需要0.06秒,而不是1.70秒。


0
正如其他人指出的那样,你的Python代码参数相差一个数量级。但我想补充一点,在编写任何类型的代码时,应尽可能避免在循环条件中使用可调用函数,就像Rafael Beckel在他的答案中指出的那样。在每次迭代中执行可调用函数会导致性能下降,这在OP的基准测试结果中是显而易见的。
虽然这个问题相对较旧,但这对于任何语言中的初学者程序员来说都是微小但有用的信息。

0

使用range的PHP代码比不使用更快。我的版本:

<?php

declare(strict_types=1);

function test(int $x): int
{
    $range = range(1, $x);
    $a = 0;
    $t1 = microtime(true);
    foreach($range as $i)
    {
        $a++;
    }
    $t2 = microtime(true);

    echo 'Time for ' . $x . ' was ' . ($t2 - $t1) . PHP_EOL;

    return $a;
}


echo test(100000000);

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