LINUX.ORG.RU

Результаты теста скорости языков C++, Java, PHP, Ocaml, Perl, Python, Ruby...


0

1

Периодически появляются темы, в которых сторонники разных языков утверждают, что их языки быстрее/лучше. Захотелось это проверить. В паре тредов в качестве примера упоминались конкретные тесты. Их я и реализовал.

Общая идея теста

Тест проводится на каком-либо примере, позволяющем проверить производительность в той или иной области.

Реализация для каждого языка должна быть максимально простой. Если для данного языка нет простой реализации поставленной задачи - такой язык не подходит для решения этой задачи. Если простого решения задачи нет ни на одном языке - такая задача не подходит для теста.

Замер выполнялся десяток раз и для каждого языка выбиралось наилучшее время. Чем время меньше - тем лучше.

.

Числодробилки: длинная арифметика

Проверялась скорость операций с длинной арифметикой. Это важно в мат.вычислениях, требующих высокой точности. Реализация на примере вычисления факториала числа 30000.

$ make
Language            real     user   system
c++                0.179    0.175    0.004
php                0.307    0.292    0.015
perl (gmp)         2.045    2.041    0.004
ocaml              2.109    1.840    0.269
ocaml (recurse)    5.015    4.743    0.271
java               8.422    8.571    0.176
ruby               8.974    8.963    0.007
python            17.609   17.602    0.004
bc                49.716   49.607    0.099
perl             123.891  123.840    0.014

Замечание: Хотя нерекурсивная версия для ocaml-а написана «не совсем» в функциональном стиле, она работает в два с половиной раза быстрее, чем рекурсивная. Почему?

.

Объекты: создание и удаление

Проверялась скорость создания и удаления объектов. Это важно в ООП-задачах при передаче объектов, как параметров. Реализация теста подсказана форумом (последнее время недоступен)

$ make
Language           real      user   system
c++ (stack)       0.614     0.613    0.000    <1 MB
c++ (heap)       11.489    11.484    0.002    <1 MB
c++ (pool)        1.929     1.909    0.001    <1 MB
java              2.703     2.511    0.185   170 MB
ocaml           100.633   100.301    0.152     1 MB
python          389.973   389.882    0.009     3 MB
php             535.231   535.064    0.011     7 MB
ruby            756.593   680.874   75.535     3 MB
perl           4549.582  4548.612    0.027     3 MB

Замечание1: Можно было бы оставить только один тест для С++, но любители Java могли бы возмутиться, что C++ выделяет объект в стеке, а Java - в heap-е, и это нечестно по отношению к Java (и к тому же не возможно при большой глубине рекурсии и больших объектах). Потому добавлен С++ heap-тест (т.к. в Java для таких объектов автоматическое управление памятью, пришлось использовать его аналог - auto_ptr в С++).

Но тут сторонники С++ могли возмутиться, что С++-ный аллокатор оптимизирован для выделения больших объектов, и работает неэффективно для частого мелкого выделения. Поэтому был добавлен тест с более подходящим аллокатором (boost::object_pool).

Замечание2: Поскольку тест работал с памятью, логично было замерять, сколько же памяти в нем используется. И из всех тестируемых языков java - единственный, потребовавший 170МБ. Остальные уложились в десяток.

.

Выводы (если очень хочется): ООП-задачи кроме как на С++ и Java эффективно решать больше не на чем (но если важна память - остается только С++). А задачи, требующие высокой точности вычислений, быстрее всего решаются на С++. Его догоняют языки, умеющие работать с сишной libgmp, хотя и они примерно на порядок отстают от С++ по скорости.

Воспроизвести результаты можно скачав архив с тестами, и выполнив make в bash-е (для замера использовался bash-евый time, чтобы заменить его на что-то свое - надо править Makefile).

Все коды GPLv3+, можно их модифицировать, дополнять своими языками и выкладывать, куда угодно. Если есть еще примеры задач, на которых можно было бы провести тест, реализации тестов на других языках, или способы улучшения имеющихся (как ускорить perl?) - предлагайте. :)

>>> Исходники теста


Самое глупое что можно придумать, это сравнивать языки при помощи каких-то задачек, и на этом основание делать выводы.

Твои тесты, замечания и выводы - зря потраченное время.

smh ★★★
()
Ответ на: комментарий от UserUnknown

> Версий компиляторов/интерпретаторов нет => в топку.
Все версии включены в архиве. :) Сообщение итак длинное, не хотелось растягивать еще больше.

sergem
() автор топика
Ответ на: комментарий от smh

> Самое глупое что можно придумать, это сравнивать языки при помощи каких-то задачек, и на этом основание делать выводы.

А что есть более умное?

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

sergem
() автор топика
Ответ на: комментарий от UserUnknown

Справедливости ради (info.txt из архива):

$ make info

Linux 2.6.27.35-170.2.94.fc10.x86_64 #1 SMP Thu Oct 1 14:41:38 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux

g++ (GCC) 4.3.2 20081105 (Red Hat 4.3.2-7)

Python 2.5.2

ruby 1.8.6 (2008-08-11 patchlevel 287) [x86_64-linux]

This is perl, v5.10.0 built for x86_64-linux-thread-multi

The Objective Caml toplevel, version 3.10.2

java version "1.6.0_0"
OpenJDK Runtime Environment (IcedTea6 1.6) (fedora-21.b16.fc10-x86_64)
OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)

PHP 5.2.9 (cli) (built: Apr 17 2009 03:29:14)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies

sergem
() автор топика

оо, наша песня хороша, начинай сначала

pained
()
Ответ на: комментарий от sergem

>Все задачи писались максимально эквивалентным кодом

В этом и ошибка. Задачи нужно решать наиболее удобным для языка способом. Если один язык медленнее другого, но позволяет быстрее и проще реализовать более производительный алгоритм, то это большой бонус первому языку.

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

Иначе лидер итак всем известен - машинные коды процессора. Вот только почему-то на них сегодня никто не пишет :)

KRoN73 ★★★★★
()

Ты какой Ocaml использовал?

Там есть три режима: (1) интерпретатор (ocaml), (2) компилятор в байт-код (ocamlc) и (3) нативный компилятор (ocamlopt). А то очень уж подозрительные результаты для окамла у тебя получились. Я так понимаю, что toplevel - это интерпретатор...

dave ★★★★★
()
Ответ на: комментарий от KRoN73

> В этом и ошибка. Задачи нужно решать наиболее удобным для языка способом. Если один язык медленнее другого, но позволяет быстрее и проще реализовать более производительный алгоритм, то это большой бонус первому языку.

Дык это выразительность языка - это вообще очень субъективное понятие. Здесь автор пытался сравнить именно _скорость языка_, а не узнать, какой язык лучше.

anonymous
()
Ответ на: комментарий от UserUnknown

>больше обидно за перл..

Дык, итак кому надо помнят, что числодробилка в нём примерно вдвое быстрее PHP-шной. Вот по объектам - на память не помню. А сайт с моим тестом объектным Фибоначчи лежит. Ну да объекты в Perl'е - это страшновато, так что и фиг с ними :)

KRoN73 ★★★★★
()
Ответ на: комментарий от dave

>А то очень уж подозрительные результаты для окамла у тебя получились

В моих числодробильных темах под виндой ещё, ocamlopt долгое время был лидером, иногда почти вдвое уделывая MSVC :)

KRoN73 ★★★★★
()
Ответ на: комментарий от anonymous

> Здесь автор пытался сравнить именно _скорость языка_, а не узнать, какой язык лучше.

Скорость языка - понятие совершенно бессмысленное. Я же уже сказал, что самым быстрым будет машинный код. Априори.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

> Скорость языка - понятие совершенно бессмысленное.

Чо?

> Я же уже сказал, что самым быстрым будет машинный код. Априори.

Замечательно, пусть будет. Я лично хочу узнать, какую цену я плачу за высокоуровневость.

anonymous
()

Чего не пойму, на кой ляд писать очередную выводилку "Hello world", когда есть: The Computer Language Benchmarks Game - shootout.alioth.debian.org

Skynin
()
Ответ на: комментарий от KRoN73

> В этом и ошибка. Задачи нужно решать наиболее удобным для языка способом. Если один язык медленнее другого, но позволяет быстрее и проще реализовать более производительный алгоритм, то это большой бонус первому языку.

Не совсем. Если цель - посчитать факториал, то цикл - не самый быстрый способ (есть более эффективные методы, в т.ч. и функция в libgmp). Но если цель - оценить скорость работы с длинной арифметикой, оценивать ее надо максимально похожими методами.

sergem
() автор топика
Ответ на: комментарий от Skynin

>когда есть: The Computer Language Benchmarks Game - shootout.alioth.debian.org

Ну, человеку хочется своими руками :)

KRoN73 ★★★★★
()

Сразу замечания по первому тесту:

1. s/c++/c++ (gmp)/g

2. s/php/php (gmp)/g

3. Ну окамл-то можно было скомпилировать в нативный код ради интереса?

Итого: плюсы, перл и пых -- тестирование, у кого обвязка к gmp круче. Окамл пересобрать компилятором в нативный код и проверить ещё раз.

kemm
()
Ответ на: комментарий от UserUnknown

> больше обидно за перл.. автор явно запускал всё это в иксах с кучей фоном висящих вещей...

Запускал через ssh в screen-е. Делалось несколько запусков, выбирался наилучший.

sergem
() автор топика
Ответ на: комментарий от Skynin

> Чего не пойму, на кой ляд писать очередную выводилку "Hello world", когда есть: The Computer Language Benchmarks Game - shootout.alioth.debian.org

Потому что там, по большому счету сравнивается знание контрибьюторами соответствующих алогоритмов и optimization tricks, а не скорость языка.

anonymous
()
Ответ на: комментарий от dave

> Ты какой Ocaml использовал? Там есть три режима ...

Использовался ocamlc (это видно в исходниках). Было:

factorial:
ocaml             2.109    1.840    0.269
ocaml (recurse)   5.015    4.743    0.271
fibonacci:
ocaml           100.633  100.301    0.152

С ocamlopt стало:

factorial:
ocaml             1.911    1.692    0.219
ocaml (recurse)   3.623    3.346    0.276
fibonacci:
ocaml            14.694    14.546   0.003

Таким образом кривая реализация на ocaml-е немного обошла perl в общей таблице. Но рекурсивная реализация все равно отстает в два раза. Зато работа с памятью стала значительно быстрее. Хотя на место в таблице это не повлияло.

PS: я бы поправил результаты в шапке, но как?

sergem
() автор топика
Ответ на: комментарий от sergem

А у тебя там хвостовая рекурсия или обычная?

dave ★★★★★
()
Ответ на: комментарий от sergem

> PS: я бы поправил результаты в шапке, но как?

никак.

что касается теста, то имхо, это бесполезное занятие, как правильно заметил Крон. Можешь сделать еще тест stackless python с хвостовой рекурсией?

val-amart ★★★★★
()
Ответ на: комментарий от val-amart

> Можешь сделать еще тест stackless python с хвостовой рекурсией?

Конечно. Код, пожалуйста. :)

sergem
() автор топика
Ответ на: комментарий от KRoN73

>> Таким образом кривая реализация на ocaml-е немного обошла perl в общей таблице.

> Дико слышать такое...

Там есть две реализации. Одна - цикл с присваиваниями, похожая на остальные программы, но явно не в функциональном стиле. А есть другая, рекурсивная. И рекурсивная - более медленная.

sergem
() автор топика

вот насчет создания/удаления объектов, что-то сильно подозрительные данные для окамла... Lionet приводил данные, когда за счет gc, ocaml был быстрее C/C++ при работе с большим кол-вом малых объектов (кажется вот тут - http://lionet.livejournal.com/37805.html?style=mine)

да и вообще, про то, что gc будет сравним по скорости с ручным выделением, писалось уже давно, еще лерой в Zinc Experiment про это писал

ott ★★★★★
()

во вторых - тесты уж больно куцые, у тебя большой оверхед на инициализацию рантайма и т.п. вещей.

в третьих, с постановкой экспериментов - совсем плохо, необходимо выполнять серию измерений, и оттуда уже брать средние данные и т.п. вещи

по поводу фибоначи - это слишком наивная версия, посмотри в SICP на предмет того, как правильно считать эти числа

ott ★★★★★
()
Ответ на: комментарий от ott

> вот насчет создания/удаления объектов, что-то сильно подозрительные данные для окамла...

Я не знаток ocaml-а. Способ тестирования известен, коды открыты. Предлагайте свою реализацию, я перезапущу тест... :) Или можете сами исправить код и запустить. :)

PS: Где же варианты реализаций от любителей хаскеля?

sergem
() автор топика
Ответ на: комментарий от ott

> да и вообще, про то, что gc будет сравним по скорости с ручным выделением, писалось уже давно

Он сравним. Я сравнил. И сравнение на порядок в пользу С++ :)

> тесты уж больно куцые, у тебя большой оверхед на инициализацию рантайма и т.п. вещей.

Большой - это сколько? 0.1 сек? А время выполнения - секунды. Оверхед не влияет на результат сравнения.

К тому же этот оверхед нельзя исключать. Когда я кликаю на иконку и запускаю программу - этот оверхед тоже есть. Так что замерять вместе с ним, во-первых, проще, а во-вторых - правильнее.

> в третьих, с постановкой экспериментов - совсем плохо, необходимо выполнять серию измерений, и оттуда уже брать средние данные и т.п. вещи

Выполнялась серия экспериментов (не меньше десятка, кроме перла, для него было всего 3 - ждать долго). Выбирался наилучший результат. Зачем наилучший? Чтобы исключить влияние других программ и демонов, случайные свопы на диск, обращения по сети и другие действия, которые могли замедлить программу.

> по поводу фибоначи - это слишком наивная версия, посмотри в SICP на предмет того, как правильно считать эти числа

Фибоначчи считаются по формуле. Но цель - не посчитать число, а посмотреть на скорость создания/удаления объектов. При таком "способе подсчета" создается около ста миллионов объектов. Так что такой способ - в самый раз. :)

А еще такой способ подсчета - рекурсивный, что позволяет не извращаться для языков, вроде ocaml.

sergem
() автор топика
Ответ на: комментарий от mv

угу - сравнивать unsigned int с arbitrary precision Int - это тонкое извращение...

ott ★★★★★
()
Ответ на: комментарий от mv

> Ты тестируешь libgmp. Смысл?

Нет. Я тестирую то, что написано в шапке - скорость работы с длинной арифметикой в разных языках. Если она реализована через libgmp - я тестирую libgmp и скорость взаимодействия с ней. Например и в php и в perl используется libgmp, но скорость работы у них разная.

Можно сказать, что я тестирую эффективность применения языков для тех или задач.

sergem
() автор топика

Окамловый код для факториала плох. Особенно в случае рекурсии, которую можно заменить здесь на хвостовую. Плюс ненужные повторяющиеся создания объектов Int 1 и Int 0, которые неплохо было законстантить через let.

dave ★★★★★
()
Ответ на: комментарий от dave

> Окамловый код для факториала плох. Особенно в случае рекурсии, которую можно заменить здесь на хвостовую.

Можно увидеть исправленный код?

> Плюс ненужные повторяющиеся создания объектов Int 1 и Int 0, которые неплохо было законстантить через let.

Проверил. Скорость, вроде бы, не изменилась.

PS: Ради этого я и создавал тему. Чтобы более опытные в других языках разработчики могли предложить лучшие варианты. И еще чтобы проверить на практике фразы вроде "java не медленнее C++" или "функциональные языки для числодробилок лучше".

sergem
() автор топика
Ответ на: комментарий от sergem

Можно сказать, что я тестирую эффективность применения языков для тех или задач.

Ну хорошо. Вот мой пример:

#include <iostream>
#include <gmpxx.h>

int main(int argc, char *argv[])
{
	mpz_class a("100000000000000000000"), b("300000000000000000000"), c;
	for (long i = 0; i < 1000000000; ++i)
		c = a + b;
	std::cout << c << std::endl;
	return 0;
}

$ g++ -O3 add.cpp -lgmpxx

$ time ./a.out
400000000000000000000

real    0m18.505s
user    0m18.424s
sys     0m0.023s
(time (let ((a 100000000000000000000)
	    (b 300000000000000000000)
	    c)
	(dotimes (i 1000000000
		  (setq c (+ a b))))
	(print c)))

$ sbcl --noinform --load add.lisp 

400000000000000000000 
Evaluation took:
  0.354 seconds of real time
  0.353947 seconds of total run time (0.353947 user, 0.000000 system)
  100.00% CPU
  989,279,707 processor cycles
  0 bytes consed

Из чего я в стиле крестовых фанбоев сделал бы вывод, что Лисп считает в 50 раз быстрее крестов. А если сложение сделать через функцию, то и во все 250.

Очевидно, что это не так. Просто для Лиспа большие целые числа - родные, поэтому он в компайл-тайме сложил два аргумента. Из чего можно сделать вывод, что gmp никак не поможет крестам соптимизировать операции над константами, отчего они глубоко всосут даже у Лиспа.

mv ★★★★★
()
Ответ на: комментарий от Deleted

>>ruby 1.8

>Оххохо.

В моем репозитории другого нет. У вас есть? Запустите тест у себя и выложите тут результаты. :)

sergem
() автор топика
Ответ на: комментарий от sergem

>В моем репозитории другого нет.

Это где такие репозитории? Даже в Убунту я 1.9 уже в прошлом году тестил.

KRoN73 ★★★★★
()
Ответ на: комментарий от sergem

Ну, для этого нужен тот же ПК, что бы цифры были точными. К тому же, я не пойму, какое значение нужно передавать в скрипты. Можно было его внутрь прописать что ли.

>print fact(ARGV[0].to_i), "\n"


use «puts», Luke :}

Deleted
()
Ответ на: комментарий от Deleted
class Fib
	def initialize(n)
		@number = n
	end

	def value()
		if(@number <= 2)
			return 1
		else
			return Fib.new(@number - 1).value() + Fib.new(@number - 2).value()
		end
	end
end

Убиццо (-_-)

Deleted
()
Ответ на: комментарий от mv

> Из чего я в стиле крестовых фанбоев сделал бы вывод, что Лисп считает в 50 раз быстрее крестов. А если сложение сделать через функцию, то и во все 250.

В таком цикле и правда непонятно, что меряется. В нем вообще можно было выкинуть цикл, и программа должна была выполниться за 0.001 сек, поскольку ее результат ни от чего не зависит. Такой тест зависит от компилятора, его умения выкидывать ненужный код... Поэтому я изначально не хотел делать тестов вроде "повторить миллион раз примитивную операцию", поскольку в реальных программах так не делается.

Вот интересно было бы увидеть такой же тест, но для факториала... Его можно было бы добавить в общую табличку. Код на С++ я уже выложил. Будет версия на lisp-е? ;)

sergem
() автор топика
Ответ на: комментарий от Deleted

> Ну, для этого нужен тот же ПК, что бы цифры были точными.

Не нужен. Относительный результат будет таким же. Абсолютные числа не важны. Важно то, что java в 50 раз медленнее работает с длинной арифметикой, чем С++, и в 25 раз медленнее PHP (гы :)

> К тому же, я не пойму, какое значение нужно передавать в скрипты.

Никакое не надо. Просто запустить make из bash-а.

Если интересно, у обоих тестов в каждой программе 1 параметр. В факториале - это число, факториал которого считается. В фибоначчи - номер числа фибоначчи.

> Можно было его внутрь прописать что ли.

Оно итак внутри. В Makefile. Я их специально туда вынес. Чтобы не пришлось редактировать 10 файлов для изменения параметра.

sergem
() автор топика
Ответ на: комментарий от KRoN73

>> Убиццо (-_-)

> Ну, это, как я понимаю, модифицированный вариант моих тестов :)

Так и есть. Я ссылку на источник указал. :)

А чем он плох? Еще раз напоминаю. Цель - не посчитать число фибоначчи. Это можно сделать и проще. Цель - замерять скорость создания и удаления маложивущих временных объектов. Есть другой способ? Предлагайте... :)

sergem
() автор топика
Ответ на: комментарий от sergem

Не нужен. Относительный результат будет таким же.

Нет. Разные платформы дают нелинейные соотношения производительности. Вплоть до того, что разные языки оказываются взаимно разными по скорости. Скажем, в числодробилке Java может сильно обходить C++ на Intel'ах и несколько отставать на AMD.

KRoN73 ★★★★★
()
Ответ на: комментарий от sergem

Что за параметр я там вижу. Но запускать незнакомые makefile'ы мне религия не позволяет :}

А вообще, что за дистр? Сейчас даже в Debian'е ruby 1.9 есть. Помнится, мы «считали» тут (и не только тут) число фибоначчи. Рекурсивно. Руби 1.9.1 был раз в 6-7 быстрее, чем Руби 1.8.x

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.