Programming
8 August 2009

Производительность C++ vs. Java vs. PHP vs. Python. Тест «в лоб»

/update/ Статья обновлена по результатам обсуждения. Поправлен код Python (около 40% ускорения), написан код на Perl и Ruby (но меня терзают смутные сомнения, что с ruby я что-то сделал неправитьно), поправлен код на Java (на моей машине корректнее тестировать int, а не long. к тому же int в Java эквивалентен long в C++).

Вопрос производительности (скорости работы) различных языков часто всплывает в комментариях, на форумах, часто необоснованные :). Встречаются статьи, в которых авторы приводят примеры, где выигрывает реализация на том или ином языке.

После прочтения очередной статьи мне захотелось самому разобраться «здесь и сейчас». Сначала захотелось сравнить Java и C++ (не верил я, что в вычислительных тестах ява может догнать и обогнать cpp). 10 минут и простой код на C++ и яве готов: простой цикл и математические операции. После написания теста подумал и перевёл их на php и python. Позже добавился код на perl и ruby.

Итак, пару слов о тесте:
Алгоритм синтетический, долгий цикл (двухуровневый) и в нём вычисление математического выражения. Таким образом оценивается вычислительная производительность самого языка (интерпретатора или скомпилированного кода), никаких привязок к качеству реализации тех или иных библиотек, никаких внешних сервисов, никаких системозависимых операций (диск, сеть, графика).

Заранее замечу:

1) Мне нравится ява и я честно предполагал, что результаты будут лучше. Обновлено: long в 64-х битных системах работает значительно быстрее. При работе с int в 32-х битных системах Java значительно ускоряется (на моей машине быстрее, чем C++, видимо, JVM оптимизирует исполнение по умолчанию)
2) Я догадывался, что php будет медленней C++ и Java, но не думал, что он окажется быстрее Perl.
3) Предполагал, что Python будет сопоставим с PHP, но ошибся. Видимо, стандартная поставка PHP лучше оптимизирует исполнение кода.
4) Я совсем не знаком с Ruby, код взят из одного из комментариев. Причём использован код 1, так как у меня он работает быстрее чем код 2. Возможно, это также связано с тем, что у меня 32bit-система.
5) Я достаточно уважительно отношусь к различным языкам программирования, эта статья ни одним из углов не нацелена на разжигание холиваров. Каждый язык имеет свою нишу и своих поклонников.

Тесты запускались по 5 раз минимум, чтобы избежать случайных всплесков. Запускались из консоли и как «nice -n -9», то есть с максимальным на данный момент приоритетом в системе.

Чтобы не заставлять вас читать всю статью, сразу приведу краткие результаты.

Диаграмма (обновленная):

Старый вариант здесь
На диаграмме слолбец с Ruby частично прозрачен по причине того, что на моей машине скрипт Ruby исполнялся неприлично долго, в то время как в комментарии указано, что скрипт исполняется в 4 раза быстрее скрипта на Python — я в замешательстве.
Столбец с Python прозрачен, так как при включении psyco скрипт ускоряется более чем в 10 раз. Проверил на своей машине. Но это, с моей точки зрения, хак, не отражающий собственную производительность языка.
Столбец с PERL, как могут заметить старожилы, теперь идёт вровень с Python 2.6. Причиной этому послужила смена кода с C-подобного синтаксиса на использование range. Дополнительную производительность (около 12%) можно получить использовав директиву «use integer;», но это, по-моему, тоже хак.

В виде таблицы (тоже обновлённой):
Язык Java Java -server C++ C++, -O2 PHP Python 2.6 Python 3.1 Perl 5.8 Ruby 1.8 Ruby 1.9(?)
Время исполнения, сек 5,3 2,8 8,5 2,6 62 91 145 91 207 ~30
Производительность, % 160 303 100 327 14 9 6 9 4.11 28

Время исполнения — на P4-1.8Ггц.
Производительность — относительно производительности базового кода на C++.

Добавлен столбец с запуском Java-кода с ключём "-server". После перехода с «long» на «int» (повторюсь, int в java такой же как и long в c++ на 32bit-arch) он начал давать прирост в производительности почти вдвое.
Столбец с Ruby 1.9 на моём железе не тестировался, результат перенесён через сравнение с производительностью Python'а на той же машине.

И, чтобы не быть голословным, тестовый код.

Java, Test01.java (int в Java то же что и long в C++):

package ru.dchekmarev.test.performance;
public class Test01 {
    public static void main(String[] args) {
//        long start = System.currentTimeMillis();
        int r = 0;
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 10000; j++) {
                r = (r + (i * j) % 100) % 47;
            }
        }
        System.out.println("answer: " + r);
//        закомментировано, т.к. замеры делаются из командной строки
//        System.out.println("run time (millis): " + (System.currentTimeMillis() - start));

    }
}

C++, Test01.cpp:

#include <iostream>
using namespace std;
int main(void) {
    long r = 0;
    for (int i = 0; i < 10000; i++) {
        for (int j = 0; j < 10000; j++) {
            r = (r + (i * j) % 100) % 47;
        }
    }
    cout << "answer: " << r << endl;
}

PHP, Test01.php:

<?php
$r = 0;
for ($i = 0; $i < 10000; $i++) {
    for ($j = 0; $j < 10000; $j++) {
        $r = ($r + ($i * $j) % 100) % 47;
    }
}
echo 'answer: ' . $r . "\n";
?>

Python, Test01.py (вынос кода в функцию ускоряет работу кода почти вдвое, отдельная же инициализация range() на моей машине даёт порядка 5% производительности):

def test():
  r = 0
  for i in range(0, 10000):
      for j in range(0, 10000):
          r = (r + (i * j) % 100) % 47
test()
print("answer: ", r)

Perl, Test01.pl (обновлено, с range работает на 25% быстрее против c-подобного синтаксиса for):

$r = 0;
# старый вариант, C-подобный синтаксис
# for ($i = 0; $i < 10000; $i++) {
#   for ($j = 0; $j < 10000; $j++) {

for my $i (0..9999) {
  for my $j (0..9999) {
    $r = ($r + ($i * $j) % 100) % 47;
  }
}
print "answer: $r\n";
Вот здесь приведён красивый пример на Perl, но, мне кажется, такой вариант уже слишком специфичен.

Ruby, Test01.rb:

r = 0
for i in 0..10_000 do
  for j in 0..10_000 do
    r = ( r + ( i * j ) % 100) % 47
  end
end
puts "answer: #{r}"


Вот здесь в комментариях обсуждают решение на erlang.

Как видите, ничего сложного: два цикла и математическое выражение. Вычислительная задача в чистом виде.

мой оригинал — там старая версия статьи, а также информация об версиях использованного ПО и результаты тестов из консоли.

Ещё раз повторюсь: каждый язык имеет свою нишу, своих поклонников и свои задачи, с решением которых он справляется лучше других.

PS: а вообще, нет смысла загоняться и меряться чем бы то ни было, производительность самого языка важна для достаточно узкого круга задач, т.к. в общем случае, системы, библиотеки и прочая обвязка нынче несоизмеримо тяжелее самой вычислительной задачи.

+23
107.5k 78
Comments 216
Top of the day