14

As an extremely simple benchmark, I executed the below simple code on PHP 7.0.19-1 and Python 3.5.3 (command line) on the same Raspberry Pi 3 model B.

Python's execution time was horrible in comparison to PHP's (74 seconds vs 1.4 seconds). Can anyone help me understand why the execution takes so much longer on Python? Is there something I'm doing wrong, or some optimizations/settings that would improve its performance to meet or exceed that of PHP? Or is Python just that much slower (surely not!)?

Yes I saw this benchmark, which reports PHP 7 blazes past the other languages, but you'd think both would be fairly equally optimized when doing such a simple operation.

Python executes the loop about twice as fast if a string assignment is substituted for the addition. But that's still 34 seconds vs about 1.1 sec.

PHP7 code:

<?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);

Results: Time for 100000 was 0.036377191543579 100000Time for 1000000 was 0.18501400947571 1000000Time for 10000000 was 1.3939099311829

Python3 code:

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))

Results: Time for 1000000 was 0.761641 1000000 Time for 10000000 was 7.427618000000001 10000000 Time for 100000000 was 74.320387 100000000

UPDATE: yes after @Amber pointed it out, I realize I totally PEBKAKed and the loop counters are an order of magnitude apart. Even so, the answers were really interesting so it was worth asking the question.

Ryan Griggs
  • 2,457
  • 2
  • 35
  • 58
  • Microbenchmarks tend to be both noisy and subject to oddities - for instance, the same Python code you have runs in a single-digit number of seconds on a random VM I tried it on. Without a consistent environment and results that reproduce, it's going to be hard to definitively answer your question. – Amber Dec 30 '17 at 05:43
  • That's why I ran both tests on the exact same hardware, same operating system, etc. I was assuming that would eliminate hardware inconsistencies and focus on interpreter speeds. – Ryan Griggs Dec 30 '17 at 05:45
  • Yes, but we the answerers do not have access to that environment, so we have no idea what factors may be affecting it. Our hardware is not yours; our system environments are not yours. – Amber Dec 30 '17 at 05:46
  • Did you also run the PHP code on that same VM? might it have run in sub-1-second time? Also, the results are consistent - multiple executions yield similar results - very little variation based on CPU load etc. – Ryan Griggs Dec 30 '17 at 05:47
  • In PHP `$a++` operates in place and just increments the value of an integer. In python `a += 1` is equivalent to `a = a + 1` and creates a new integer which then replaces the old `a` in the namespace. – Klaus D. Dec 30 '17 at 05:51
  • 4
    Note that the PHP code you have has one fewer zero on the numbers than the Python code does. The largest number in your PHP code is `10000000` (7 zeroes), the largest for Python is `100000000` (8 zeroes). – Amber Dec 30 '17 at 05:51
  • These results are similar on my Windows platform too. Python3 takes 3.6 seconds, where PHP 7 takes 0.18 seconds. PHP is still 20x faster. – Ryan Griggs Dec 30 '17 at 05:52
  • 2
    I have only one reaction: hahahahahahaha – Amir Hassan Azimi Oct 08 '18 at 16:00

5 Answers5

14

They're both within an order of magnitude of each other, when you run them with identical cycle counts rather than having the Python counts being larger by an order of magnitude:

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.5708s

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))
Amber
  • 507,862
  • 82
  • 626
  • 550
  • I'm a complete idiot. You're exactly right, I mis-counted the number of zeros and just assumed Python was hugely inefficient. PEBKAC! Thanks for pointing this out. Now I can get back to doing something useful! :) – Ryan Griggs Dec 30 '17 at 05:56
  • PHP [could support underscores in numeric literals](https://phpinternals.net/articles/implementing_a_digit_separator) like Python 3.6, but I prefer scientific notation: `1e8` instead of `int(1e8)` in Python. – Cees Timmerman Jul 03 '18 at 12:28
8

You guys are not being fair. The two pieces of code are NOT doing the same thing.

While PHP only increments two variables ($a and $i), Python is generating a range before it loops.

So, to have a fair comparison your Python code should be:

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

Aaaaaaand, it's MUCH faster:

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

VS

>>> print(test2(100000000))
Time for 100000000 was 3.079545
Rafael Beckel
  • 2,199
  • 5
  • 25
  • 36
2

The loop itself appears to be twice as slow in 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)))

However, that is only the standard implementation of Python which cares more about being easy to understand than being fast. PyPy3.5 v6.0.0 for instance, runs that empty loop in 0.06s instead of 1.70s on my laptop.

Cees Timmerman
  • 17,623
  • 11
  • 91
  • 124
0

As others have pointed out, your arguments are an order of magnitude off for the Python code. But I just want to add that callables in loop conditions should be avoided as much as possible whenever writing any kind of code as Rafael Beckel pointed out in his answer. On every iteration the callable is executed, which results in bad performance as is evident in OP's benchmark results.

While this question is relatively old, this is just tiny but useful info for budding programmers in any language.

Richard
  • 1,702
  • 2
  • 11
  • 17
0

PHP code using range is faster than without. My version:

<?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);
LONGMAN
  • 980
  • 1
  • 14
  • 24