Пользователь
0,0
рейтинг
8 августа 2009 в 15:47

Разработка → Производительность 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: а вообще, нет смысла загоняться и меряться чем бы то ни было, производительность самого языка важна для достаточно узкого круга задач, т.к. в общем случае, системы, библиотеки и прочая обвязка нынче несоизмеримо тяжелее самой вычислительной задачи.
Дмитрий @dzmitryc
карма
43,2
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (216)

  • –19
    А почему вы в одно сравнение вынесли тесты консольных приложений (c++ и java) и тесты под вэб, которые выполняются на вэб-сервере? По-моему их так сравнивать неуместно.
    • +9
      Как видно по ссылке на оригинал, php и python точно так же тестировались в консоли. Никто не заставляет использовать эти языки только для веба :) Как и никто не запрещает писать cgi на ассемблере. Как и говорил автор, у каждого языка своя ниша.
      • +4
        И еще, тот факт, что long и int в java не одно и тоже, что в c++
        100000000 операций с областями памяти в 8 и 4 байта не одно и тоже, что с 4 и 2 байта.
        • +1
          насчет области памяти в 2 байта — это очень вряд ли, отдельные переменные выравниваются по машинному слову
          кстати, если уж на то пошло, в питоне вообще разрядность числа ограничена виртуальной памятью
        • +1
          Вы оказались правы, long в Java съедал всю малину
      • +7
        «Из чего лучше пить кофе: чайник, чашка, трамвай или абрикос?»
    • +2
      При времени выполнения кода в 200 сек затраты на создание контекста несущественны.
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        А в некоторых местах они будут медленней из-за сборщика мусора.
  • –8
    Спасибо, все просто и наглядно
    • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    Можно было бы и С# протестить.
    • +1
      К сожалению, сейчас у меня нет такой возможности. Но, уже после написания статьи, пришла мысль, что действительно стоило бы тестировать бОльший набор языков и по бОльшему числу параметров (кроме циклов взять создание классов, вызовы методов, простых функций, статический, динамический контекст).
      Думаю, в скором времени будут другие тесты — самому всё ещё любопытно.
  • –11
    Спасибо знал что C++ лучший :)
    • +4
      у каждого языка своя ниша как сказал автор. не существует лучших языков.
      • 0
        какая ниша у brainfuck или у HQ9+? =)
        • +3
          из названия первого всё ясно
        • 0
          HQ9++ можно слегка доработать (до HQ9++T например), и потом использовать студентами в курсах «написать задание на любом ОО-языке» в виде сдачи иходников программы main.hq9:«T» и её компилятора (а команде Т в рамках языка будет совершенно случайно реализовываться то, что требовалось по заданию)

          соррри за бред, была такая идея сделать так в этом семестре, только заломало почему-то :)
    • 0
      а может ASM?
      • +3
        я не думаю, что асм будет сильно отличаться от c++
        • +3
          какой идиот вас заминусовал?
        • НЛО прилетело и опубликовало эту надпись здесь
          • +3
            Что-то писать тут мне уже страшно )))
            • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Более того, иногда компилятор лучше «знает» как создать более оптимизированный код под конкретную систему/процессор. И хороший asm-код для одной системы может уступать С++ коду на другой. Да и вообще в таких тестах, говоря что тестируют язык, должны понимать что компилятор/транслятор и есть этот самый язык. А в асме компилятор, по большому счету это программист :)
        • 0
          На самом деле асм отрабатывает (на x86) где-то в полтора раза быстрее чем неоптимизированный C++ код и медленнее, чем оптимизированный (всё-таки gcc не зря флажки разные имеет :) )
          • 0
            Странное заявление, тот же компилятор С++ переводит все в тот же «асм» (команды проца). Потому как может быть асм быстрее асма?
            • +2
              Просто компилятор лучше оптимизирует, чем я
      • 0
        На асме выходит чуть быстрее, чем C++ без оптимизации, общий алгоритм такой (если кто-то знает как улучшить — велкам):

        # %ecx = r = 0
        xorl %ecx, %ecx
        movl $10000, %eax

        _loop1:
        movl $10000, %ebx

        _loop2:
        # сохранили
        pushl %ebx
        pushl %eax

        # eax = i * j
        decl %eax
        decl %ebx
        mull %ebx

        # edx = eax % 100
        xorl %edx, %edx
        movl $100, %ebx
        divl %ebx

        # ecx = ecx® + edx
        addl %edx, %ecx

        # ecx = ecx % 47
        movl %ecx, %eax
        xorl %edx, %edx
        movl $47, %ebx
        divl %ebx
        movl %edx, %ecx

        # восстановили регистры
        popl %eax
        popl %ebx

        decl %ebx
        jnz _loop2
        decl %eax
        jnz _loop1

        # здесь в %ecx имеем результат

        общий вид здесь: dchekmarev.ru/blog/article/1249836445
    • –2
      ок, лол.
  • 0
    а почему оптимизация использовалась только для c++, а не для всех? я не говорю о том, что какой то язык при оптимизации может обогнать другой. просто было бы очень интересно на это глянуть.
    • –1
      Просто потому, что я знал, как включить её в компляторе (gcc). Но референсной была выбрана версия без оптимизации.
      Кстати, не подскажете, как её включить для python/java (пока не силён в опциях)?
      • +10
        Про Python:
        Замените range() на xrange(),
        Для некоторой оптимизации можно запускать файл, как `python -O ./Test01.py` (в ключе буква, а не цифра),
        либо использовать сторонние библиотеки типа psyco.
        • 0
          Замена range на xrange не всегда даёт прирост производительности, кстати. Иногда бывает быстрее просто породить весь список целиком, чем «генератором» «генерить» :)
          • 0
            Попробовал сейчас range и xrange, после первого прогона разница около 20 секунд.
            Даже учитывая то, что кроме python'a на компьютере выполняется куча других программ, разница достаточная.
            • 0
              я таки тоже протестил и с xrange время выросло на ~10 секунд :)
              • 0
                В моем случае xrange ускоряет на 2 секунды (17 против 19 секунд).
                Использовать range имеет смысл обычно для небольших диапазонов.
        • 0
          Такой заменой код только замедлится:
          range — создает список в памяти.
          xrange — возвращает обьект-итератор.

          Использование xrange приводит к экономии памяти.

          Кроме того, тест выполнялся на python3, где xrange вообще отсутвует, его функции перебрал на себя просто range.
      • +6
        без замены range на xrange
        без psyco 36.94 сек
        c psyco.full() 4.38 сек

        с заменой range на xrange
        без psyco 35.44 сек
        c psyco.full() 4.55 сек
      • 0
        Для Java нужно как минимум запускать ява-машину с ключом -server (то есть в режиме для сервера). В нем больше ресурсов затрачивается на JIT-компиляцию при первоначальном запуске.

        Еще на самом деле стоит удлинить время выполнения теста, чтоб время JIT-компиляции меньше влияло на результат.
  • 0
    Все-таки, неплохо было бы увидеть в тесте Perl и Ruby.
    • 0
      Упс, Perl я как раз знаю, просто забыл :) А вот с Ruby пока несколько сложнее
      • +1
        Ruby. Можно 1.8 и 1.9
        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 r
        
        • 0
          У меня в версии Ruby 1.8 выдаёт, гм, «плохие результаты». Почти вдвое медленней питона 2.6. Это нормально или я что-то не так делаю?
          • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          в Руби правильно так
          r = 0
          10000.times do |i|
          	10000.times do |j|
                  	r = (r + (i * j) % 100) % 47
              	end
          end
          puts "answer: #{r}"
          
          
          • +1
            ruby 1.9.1p129
            real    0m13.097s
            user    0m13.074s
            sys     0m0.020s
            


            Python 2.4.3
            real    0m50.666s
            user    0m50.580s
            sys     0m0.052s
            
            • +1
              в ruby 1.9 прикрутили JIT
              в python его роль, когда нужно, выполняет psyco
              • +1
                Ткните пальцем, где инфа про JIT в MRI 1.9?

                Про Python зная, что JIT есть в PyPy и psyco.
                Про Ruby знаю, что JIT есть в JRuby, Rubinius (типа PyPy), MacRuby, IronRuby, Ruby.NET. В случае JRuby это JVM JIT, в случае IronRuby и Ruby.NET — CLR JIT.
  • –1
    Это однопроходные тесты? Запустили-померяли и в таблицу?
    • 0
      Нет, для надёжности запускал по 5 раз. С «nice -n -9», дабы никто не мешал. На большее не хватило терпения. Когда убедился, что числа более-менее постоянны, брал наиболее близкий к среднему результат.
      • +3
        Надо брать минимум, потому что никакие посторонние процессы не могут заставить процесс выполняться быстрее, чем он может, только медленнее :). Об этом писал Крис Касперски
  • 0
    почему в коде на Java делается операция вывода на экран, а после этого только замеряется время
    System.out.println(«answer: » + r);
    System.out.println(«run time (millis): » + (System.currentTimeMillis() — start));
    • –1
      это вообще может повлиять?
      • 0
        проверил, разница 1 мс
        • +1
          в смысле 0.1 сек
      • +1
        да! у меня разница в 0.2 сек, а если расчет делать как делается для C++ то получается разница в секунду. это на C2D 1.86, т.е. у автора она будет еще больше.
  • 0
    Сравнил c++, mono, java на:

    • 0
      model name: Intel® Core(TM)2 Quad CPU Q6600 @ 2.40GHz

      c++ без оптимизации:

      answer: 39
      real 0m0.872s
      user 0m0.840s
      sys 0m0.008s

      С оптимизацией -О2

      answer: 39
      real 0m2.682s
      user 0m2.672s
      sys 0m0.000s

      mono:
      answer: 39
      run time (millis): 3731.452

      java:
      #time java ru.dchekmarev.test.performance.Test01
      answer: 39
      run time (millis): 877

      real 0m0.992s
      user 0m0.932s
      sys 0m0.012s

      Так-что никакиь 16 секунд нет ;)
      • НЛО прилетело и опубликовало эту надпись здесь
        • –1
          Эээ… Как Вы это сделали?
          Попробовал на одном из серверов:

          $ cat /proc/cpuinfo |fgrep 'model name'
          model name: Dual-Core AMD Opteron(tm) Processor 2218
          model name: Dual-Core AMD Opteron(tm) Processor 2218
          model name: Dual-Core AMD Opteron(tm) Processor 2218
          model name: Dual-Core AMD Opteron(tm) Processor 2218

          # time nice -n -9 java ru.dchekmarev.test.performance.Test01
          answer: 39
          run time (millis): 4653

          real 0m6.540s
          user 0m4.697s
          sys 0m0.017s
          • +1
            Версия явы:
            java version «1.6.0_14»
            Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
            Java HotSpot(TM) 64-Bit Server VM (build 14.0-b16, mixed mode)

            Может стоит попробовать запустить яву с ключом -server?
            • –1
              "-server" прироста не даёт, как дома, так и на машине, что комментом выше. Поэтому сразу без него и пускал. Пока что не знаю почему.
              Кажется, я понял, почему у меня так тормозит: 32-х битная версия. Сейчас попробую найти среди контактов кого-нибудь с 64-х битной и потестить
            • 0
              Насколько я знаю, -server влияет только на gc. Маловероятно, что это что-то поменяет во времени выполнения данного алгоритма.
              • 0
                -server еще влияет на JIT. Но для простого алгоритма вполне вероятно может не быть разницы.
                • 0
                  На самом деле запускать надо только с ключем -server. в 1.6 java на сколько я знаю по умолчанию запускается с ключем -server.
                  Разница в том, что в клиенской части, выделяется минимальная память, и минильный кэш, и по мере необходимости ресурсы добавляются, что весьма тормозит выполнение кода. В серверной части такого нету.

                  P.S. правильней все-таки считать время выполнение не по запуску приложения, а внутри приложения(потому что язык java все-таки язык интерпретатор). к примеру:
                  (количество итераций можно задавать через параметр при запуске)
                  package com;

                  public class Test01 {
                  public static void main(String[] args) {
                  final int countIteration;
                  long totalWorkingTime = 0;
                  if (args != null && args.length > 0) {
                  countIteration = Integer.parseInt(args[0]);
                  } else {
                  countIteration = 10;
                  }

                  for (int k = 0; k < countIteration; k++) {
                  final 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;
                  }
                  }
                  final long iterationTime = System.currentTimeMillis() — start;
                  totalWorkingTime += iterationTime;
                  System.out.println(«time:» + iterationTime + "\nanswer: " + r);
                  }
                  System.out.println(«Total average time: » + (totalWorkingTime / countIteration));
                  }
                  }
          • НЛО прилетело и опубликовало эту надпись здесь
  • –2
    На моем C2D 1.86 тест на java выполняется за 8.1 сек, т.е. быстрее чем на P4-1.8.
  • +8
    По уму, код на питоне должне быть такой:

    r = 0
    i1 = range(0, 10000)

    for i in i1:
      for j in i2:
        r = ...
    
    print r
    


    зачем 10000 раз генерить второй список (range)?

    Время выполнения упало с 1.82 с при вашем варианте к 1.16 с при моём.
    • +3
      psyco даёт еще чуть-чуть выигрыша, но я не думаю, что это честно. в пыхе же (явно) не используются никакие «ускорители».
      • +2
        psyco даст не совсем даже не «чуть-чуть выигрыша» на этом примере

        без него:
        real 0m37.201s
        user 0m36.144s
        sys 0m0.142s

        с psyco:
        real 0m3.104s
        user 0m2.895s
        sys 0m0.036s

        Ускорение в 12+ раз. Если проецировать на результаты питона из статьи, с psyco должно быть быстрее, чем java, и немного недотягивать до c++.
        • –1
          У меня совсем другие цифры. Всё что пишется про цифры и рост/падение производительности сильно зависит от окружения и способа тестирования. Имхо.
          • +2
            Не вижу не одной причины, по какой psyco не ускорит этот код раз в 10 в любом окружении. Как раз для него задачка)

            def test():
                r = 0
                rng = range(0, 10000)
                for i in rng:
                    for j in rng:
                        r = (r + (i * j) % 100) % 47
                print("answer: ", r)
            
            if __name__ == '__main__':
                import psyco
                psyco.bind(test)
                test()
            
            
            • +3
              Я просто не умею готовить psyco :) Прошу прощения, в таком варианте действительно время с 1m12s упало до 0m6s :-D

              Я-то тупо psyco.full() запускал, как quickstart guide написано :).
              • +2
                psyco.full() тоже работает, секрет в том, что код вынесен в функцию.
            • +2
              Спасибо, действительно в 10 раз. Получается psyco можно применять только в функциям, а к коду на верхнем уровне он бессилен?
              • +1
                Видимо, так. Могу только предположить, что при работе psyco.full() psyco требуется получить код, который компилировать, для этого он ставит хук на вызовы функций и при обращении компилирует их по возможности.
              • +2
                нашел в документации: Psyco only compiles functions. It will not accelerate any code that runs outside any function, like…

                psyco.sourceforge.net/psycoguide/unsupported.html
        • 0
          Можете привести весь код? У меня тоже не дало результата.
      • 0
        И да, это, конечно же, не честно. Но раз уж и так сравнение кода интерпретируемого с кодом компилированным, почему бы JIT не использовать.
        • 0
          Я, кстати, не разбирался особо с psyco никогда. Он не средствами языка это делает, случаем? :)
          • +1
            там заброшенная и неподдерживаемая черная магия)
            Силы автора направлены на PyPy, интерпретатор питона с JIT, реализованный на питоне (вот тут все делается средствами языка), который должен все ускорить до небывалых высот.
    • 0
      Мой опыт с Python — чтение «start with python» :) Сейчас пропишу в статье вашу правку, спасибо
    • +2
      Испробовал python 3, unladen-swallow все в llvm, psyco:

      Python 3 почему то главный тормоз:
      • +2
        Python 3:
        mkrivushin@root:~/w/python$ python3 test_3.py
        70.3983209133
        answer:  39
        

        Unladen Swallow:
        mkrivushin@root:~/w/python/unladen-swallow$ ./python -O ../test.py
        44.4158890247
        ('answer: ', 39)
        mkrivushin@root:~/w/python/unladen-swallow$ ./python -O -j always ../test.py
        34.8978021145
        ('answer: ', 39)
        

        Psyco:
        mkrivushin@root:~/w/python$ python test.py
        6.42809700966
        ('answer: ', 39)
        

        Жалко, что пока unladen не так крут.
    • 0
      А если вместо range использовать xrange что-то изменится?
      • 0
        Выше уже обсудили, но во мнениях так и не сошлись.
        • 0
          надо действительно в данном случае не «for x in range» а «variab = range(1,1000); for x in variab»
  • 0
    никаких привязок к качеству реализации тех или иных библиотек, никаких внешних сервисов, никаких системозависимых операций

    А как насчет вывода на консоль? Все-таки операция я потоками (streams)…
    • 0
      Пардон, с потоками
  • +1
    Интересно посмотреть как в эту картину впишется код на пыхе с использованием range в цикле.
  • +3
    вы ещё к java добавте пару ключиков и прогоните этот тест раз 100 что бы java машина прогреласть

    -server
    -agressiveots
    и тд
  • 0
    ОС: winxp sp3.
    Язык: jscript.
    Процессор: Intel Core 2 Quad CPU Q6600 2.40GHz.
    Память: DDR3 4096 MBytes.
    Проходов: 50.
    Среднее время: 33634 мс.
    Разброс: 33531 мс — 33703 мс.

    • 0
      И какая тут платформа, т.е. браузер ну или точнее JS движёк?
      • 0
        windows script host.

        P.S.: Накладные расходы на каждый тест в сумме менее 0.1 мс, точнее не получается.
    • +1
      Эти пипиркомерки так же бессмысленны, как и увлекательны :)
      Вот результат для Javascript, Google v8.

      1 var r = 0;
      2 for(var i=0; i<10000; i++) {
      3 for(var j=0; j<10000; j++) {
      4 r = (r + (i * j) % 100) % 47;
      5 }
      6 }
      7 print(r+"\n");

      ~/tmp$ time ~/Projects/V8/v8/shell ./dzmitryc.js
      39

      real 0m3.243s
      user 0m3.194s
      sys 0m0.022s
  • +6
    Как сказал мой очень хороший друг — сравнивать так данные языки на равных несерьезно, т.к. одно написаны на другом.
    Заодно он подкинул мне ссылку на более подробное сравнение языков, которой я бы, в свою очередь, хотел поделиться с Вами — http://shootout.alioth.debian.org/
    • –7
      О друге скажу следующее — человек очень серьезно занимается программированием. В данный момент работает над своей операционной системой и языком программирования. Не планирует сделать, а уже написал рабочие прототипы, которые развивает дальше.
      • 0
        Расскажите еще про вашего друга.
    • –1
      Я и написал, что это синтетический тест, который показывает просто производительность :)
      А за ссылку спасибо, да, там куда серьёзнее
    • 0
      А в чём проблема такого сравнения? Сейчас границы «специфичности» слегка стираются, и каждый из приведённых языков вполне прокатывает в качестве языка «общего назначения». Стало быть, двойной цикл + вычисление математического выражения не выглядит ни для одного из языков совсем уж чужеродным.

      Ну да, я понимаю, что на одних пишут веб-скрипты, на других игры, на третьих апплеты и т.д. Тут понятно, на С++ апплет не напишешь. Но если язык используется именно как general purpose language, почему бы не устроить простой тест производительности?
      • 0
        Если быть уж совсем честным — а какой Вы результат ожидаете от такого сравнения? Что он сподвигнет изучать тот или иной язык? У каждого языка, все-таки, своя специфика применения. Опять же, в большинстве своем интерпретируемый язык будет уступать. Опять же, какая модель вычисления используемся, какова реализация? Большинство интерпретируемых языков используют стековую ВМ, которая по производительности почти на порядок уступает регистровой.
        Да, мне было интересно увидеть эти цифры, но это всё настолько относительно…

        • +1
          Да мне многого и не надо — предположим, ко мне приходит приятель и говорит: я запрограммировал расчёт такой-то математической модели, она у меня работает на языке Х и считает примерно сутки задачу среднего размера.

          Тогда я смогу сказать что-то вроде: перейдя на язык Y, ты смог бы посчитать задачу среднего размера за 10 часов. Вот и всё.
          • 0
            Тут я — согласен. Все равно, не стоит забывать, что изучение языка — дело ни одной минуты. У каждого языка есть свои особенности, тонкости и «узкие» места.
  • 0
    model name: Intel® Core(TM)2 CPU 6300 @ 1.86GHz
    gcc 4.3
    real 0m1.586s
    user 0m1.500s
    sys 0m0.024s

    gcj 4.3
    real 0m7.114s
    user 0m6.952s
    sys 0m0.028s

    java 1.6.0_14
    real 0m7.500s
    user 0m7.264s
    sys 0m0.012s

    java 1.6.0_14 server
    real 0m0.116s
    user 0m0.060s
    sys 0m0.016s

    python2.6
    real 0m57.403s
    user 0m55.363s
    sys 0m0.272s
  • +1
    Я не удивлен тем что C++ сделал Java, на то он и сложней чтобы быстрей работал.

    Но я совсем не ожидал, что PHP окажется быстрей Питона, на котором работает известный всем своей производительностью Джанго. Приятно удивлён.

    Тем не менее, здесь результаты сравнения PHP и Python другие

    shootout.alioth.debian.org/debian/benchmark.php?test=all&lang=php&lang2=python&box=1
    • 0
      По вашей ссылке профессионалы в своих языках оптимизируют алгоритмы от и до, стараясь выжать по максимуму. Автор же этой заметки вообще в питоне не в зуб ногой, чего вы хотели?: )
      • 0
        > По вашей ссылке профессионалы в своих языках оптимизируют алгоритмы от и до, стараясь выжать по максимуму.

        Что-то подсказывает мне, что мы в этом посте тоже к этому придём коллективными усилиями :)
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      The «C is Efficient» Language Fallacy переходим на OCaml? :)
      • +1
        кстати насчет ocaml:

        let r = ref 0;;

        let tstfunc x =
        for i = 1 to x do
        for j = 1 to x do
        r := (!r + (i * j) mod 100) mod 47;
        done;
        done;;
        tstfunc 10000;;
        Printf.printf «Result %d\n» !r;;

        и дальше
        $ ocamlopt speedtest.ml -o sptest
        $ time ./sptest
        Result 39

        real 0m6.364s
        user 0m6.136s
        sys 0m0.044s

        6 секунд на amd 4000+
        • +1
          на той же машине лисп cmucl
          (defun tst () (let (( r 0)) (dotimes (i 10000) (dotimes (j 10000) (setf r (mod (+ r (mod (* i j) 100)) 47) ) )) r))

          * (compile-file «test.lisp»)
          * (load «test»)
          ; Loading #p"/home/w/sandbox/test.x86f".
          T
          * (time (tst))
          ; Compiling LAMBDA NIL:
          ; Compiling Top-Level Form:

          ; Evaluation took:
          ; 10.62 seconds of real time
          ; 10.296644 seconds of user run time
          ; 0.032002 seconds of system run time
          ; 20,399,873,134 CPU cycles
          ; 0 page faults and
          ; 0 bytes consed.
          ;
          39

          То есть 10 сек
          • +1
            то же на haskell

            module Main where

            loop1 :: Int -> Int -> Int
            loop1 0 x = x
            loop1 n x = loop1 (n-1) (loop2 10000 x n)
            loop2 :: Int -> Int -> Int -> Int
            loop2 0 r _ = r
            loop2 j r i = loop2 (j-1) ((r + ( (i * j) `rem` 100)) `rem` 47) i

            main = do
            putStr «Result „
            print (loop1 10000 0)

            $ghc -O test.hs -o tesths
            $time ./tesths

            Result 39

            real 0m7.442s
            user 0m7.316s
            sys 0m0.028s

            7 c хвостиком (примерно полвосьмого) секунд

            • НЛО прилетело и опубликовало эту надпись здесь
              • +1
                на эрланге пробовали ниже по обсуждению. вроде как не быстрее получается.
              • 0
                Да тут совсем не хаскелльный код, уж очень императивный. Даже смотриться уродливо (автор ни при чем). Имхо, тут как раз можно попробовать реализовать нормальными средствами, не переписывая алгоритм один-в-один — уж слишком хаскеллю это не идёт.
              • 0
                module Main where

                myfunc :: [Int] -> Int
                myfunc r = foldl (\ r1 i -> foldl (\r2 j -> (r2 + (i*j) `rem` 100 ) `rem` 47) r1 r) 0 r

                main = do

                putStr «Result „
                print (myfunc [0..10000])

                time ./test1
                Result 39

                real 0m7.503s
                user 0m7.216s
                sys 0m0.032s

      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          1) Скорее наоборот. С С++ мегаофигены пока не надо следить за памятью. Иначе рискуешь воткнуться в sigsegv.
          2) ocaml и erlang очень разные языки, «окамли и эрланги» — непоятно по какому признаку вы их обьединили. По наличию gc? так в python и java он тоже есть. По «функциональности» языка? так ocaml позволяет писать не функционально и приведенный пример как раз написан императивно… Поясните пожалуйста вашу мысль.
          • НЛО прилетело и опубликовало эту надпись здесь
            • +2
              Valgrind хорош, спору нет, но он не панацея. Нужно иметь полный code coverage при тестировании, чтобы его вывод имел смысл. Если ваши тесты не покрывают всего кода, (там к примеру обработки ошибок или редко встречающиеся условия), то valgrind будет просто не в курсе о том, что там может быть что-то с памятью.
              То есть, как писал Дейкстра " Тестирование может доказать наличие ошибок, но никогда не докажет их отсутствие". Поэтому образно говоря в языках с встроенным управлением памяти надо постараться, чтобы слохопотать NullPointer, а в C надо очень стараться чтобы не схлопотать.
              • НЛО прилетело и опубликовало эту надпись здесь
  • +5
    В Питоне надо вставить весь код в функцию. В вашем варианте переменные записываются в массив globals(), что сильно влияет на результат. После таких изменений, код начал работать более, чем в полтора раза быстрее. Ну и range надо заменить на xrange, который и следует использовать в таких циклах. С Psyco, как описано выше в комментах, код будет работать еще быстрее.

    def main():
        r = 0
        for i in xrange(0, 10000):
            for j in xrange(0, 10000):
                r = (r + (i * j) % 100) % 47
    
        print("answer: ", r)
    
    main()
    
  • +3
    Кстати, код достаточно сильно не эквивалентен.
    Например, время старта VM у java не учитывается, а у php/python — идёт в плюс.
    • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Интереса ради — C#:

    int r = 0;
    DateTime before = DateTime.Now;
    for (int i = 0; i < 10000; i++)
    {
       for (int j = 0; j < 10000; j++)
       {
          r = (r + (i * j) % 100) % 47;
       }
    }
    DateTime after = DateTime.Now;
    Console.WriteLine(after.Second * 1000 + after.Millisecond -
                                  before.Second * 1000 - before.Millisecond);
    

    выполняется в среднем 3620 мс (Pen D 3200)
    Если для r int заменить на long, время возрастает примерно в 1,7 раз
    • 0
      Сделал аналогичный замер, Dual-Core -1.46 — 3450мс
    • 0
      Кстати, когда я меняю на Long время уменьшается на 100мс
      • 0
        вы под x64, наверное
        • 0
          Нет, чистые x32 =)
          • 0
            1394 мс — C2D T8100 2.1GHz при сборке debug, 1239 сборка releaze в MSVS'08
    • 0

      странно но у меня ваш код другие цифры показал
      • 0
        1) x64
        2) архитектура Core всяко лучше
    • +2
      Немного оффтоп: для измерения времени выполнения лучше использовать System.Diagnostics.Stopwatch, т.к. DateTime.Now берется из установок ОС, а Stopwatch замеряет время выполнения конкретного потока.
      Хотя не думаю, что в данном случае это сильно повлияло на результат.
      • 0
        спасибо, не знал :)
    • 0
      Используйте только Stopwatch для замеров производительности. с DateTime время может быть какое угодно.
  • 0
    Графики лучше бы обновить после корректировок — не все ведь пост до конца читают.
  • 0
  • 0
    Эээ, а почему бы не считать разницу в тиках от начала анализируемого кода, до его конца во всех языках, а не только в java?
    • –1
      На самом деле и для явы результат брался из консольной «time». Время запуска JVM большой роли при тесте не играло (было в пределах 5%)
      • 0
        Кстати в Python3.0 код

        from time import clock

        clock()
        r = 0.0
        i = 0.0
        while i < 10000.0:
        j = 0.0
        while j < 1000.0: # !!!
        r = (r + (i * j) % 100.0) % 47.0
        j += 1.0
        i += 1.0
        print("answer: ", r, clock())
        exit()

        Выдаёт 10.19, ну а с j<10000 в 10 раз больше)

        Если использовать int вместо float результат ухудшается примерно на 14%.
        Дело в том, что в Python целые числа хранятся специальным образом, позволяя производит вычисления с оооочень большими значениями (a = 1024**1024)
        • 0
          Да, простите Dual Core 1.7 GHz
  • +5
    >>answer: 39</>
    соппсно, тест изначально составлен неверно — нужно, чтобы получалось 42
  • +1
    -module(test).
    -export([start/0]).
    start() -> lists:foldl(fun(I, R) -> lists:foldl(fun(J, R2) -> (R2 + (I * J) rem 100) rem 47 end, R, lists:seq(0, 10000)) end, 0, lists:seq(0, 10000)).

    Eshell V5.7.2 (abort with ^G)
    1> c(test).
    {ok,test}
    2> timer:tc(test, start, []).
    {31674407,39}

    31сек
    • +1
      erlang ;)
    • +1
      а почему не c(test,[native])?
      • +1
        -module(test).
        -export([start/0]).
        start() ->
        Seq = lists:seq(0, 10000),
        lists:foldl(fun(I, R) -> lists:foldl(fun(J, R2) -> (R2 + (I * J) rem 100) rem 47 end, R, Seq) end, 0, Seq).

        Eshell V5.7.2 (abort with ^G)
        1> c(test, [native, {hipe, o3}]).
        {ok,test}
        2> timer:tc(test, start, []).
        {27637857,39}
        3> c(test).
        {ok,test}
        4> timer:tc(test, start, []).
        {24428678,39}

        p.s. AMD Athlon(tm) X2 Dual Core Processor BE-2300
        • +3
          делаем так:

          -module(test).
          -export([start/0,run/0]).
          start() ->
          Seq = lists:seq(0, 10000),
          lists:foldl(fun(I, R) -> lists:foldl(fun(J, R2) -> (R2 + (I * J) rem 100) rem 47 end, R, Seq) end, 0, Seq).

          run() ->
          loop1(10000,0).
          loop1(0,X) -> X;
          loop1(N,X) ->
          X1 = loop2(10000,X,N),
          loop1(N-1,X1).

          loop2(0,X,_) ->X;
          loop2(J,R2,I) ->
          R = (R2 + (I * J) rem 100) rem 47,
          loop2(J-1,R,I).

          3> c(test,[native]).
          {ok,test}
          5> timer:tc(test,start,[]).
          {28965824,39}
          6> timer:tc(test,run,[]).
          {7921692,39}
          7>

          То есть вашим вариантом на моей машине — 28 сек, переписаным — 8 сек.
          • 0
            Ну тогда такой вариант :)

            run2() -> dotimes(10000, fun(I, R) -> dotimes(10000, fun(J, R2) -> (R2 + (I * J) rem 100) rem 47 end, R) end, 0).
            dotimes(0, _, Acc) -> Acc;
            dotimes(T, F, Acc) -> dotimes(T-1, F, F(T, Acc)).

            1> c(test, [native]).
            {ok,test}
            2> timer:tc(test, start, []).
            {22927752,39}
            3> timer:tc(test, run, []).
            {10201462,39}
            4> timer:tc(test, run2, []).
            {11048724,39}

            А какая версия erlang'а у вас? У меня такие результаты на erlang R13b.
            • +1
              А я тестировал на R12B5.

              Попробовал на R13B1 —
              6> timer:tc(test,start,[]).
              {19239320,39}
              7> timer:tc(test,run,[]).
              {7114840,39}
              8> timer:tc(test,run2,[]).
              {8048659,39}

              На R12B5 — 2> timer:tc(test,start,[]).
              {28226647,39}
              3> timer:tc(test,run,[]).
              {7789521,39}
              4> timer:tc(test,run2,[]).
              {8189215,39}

              Другими словами, Ваш первый вариант при переходе с R12 на R13 ускорился почти на треть — с 28 сек до 19 сек. Однако…
              А рекзультаты на функциях улучшились чуть-чуть с 7.7 и 8.1 до 7.1 и 8.0 сек…

            • +1
              Посмотрел, почему run2 получился немного медленнее чем run (хотя по смыслу это вроде то же самое).

              После дизассемблирования beam файла оказалось, что loop1 и loop2 адресуются статически, типа
              {move,{integer,10000},{x,0}}.
              {move,{x,2},{y,0}}.
              {call,3,{test,loop2,3}}. < == Вот оно
              {gc_bif,'-',{f,0},1,[{y,0},{integer,1}],{x,2}}.
              {move,{x,0},{x,1}}.
              {move,{x,2},{x,0}}.
              {call_last,2,{test,loop1,2},1}.

              А в dotimes вызывается преданная ей функция, которая в свою очередь создает переменную-функцию.
              Типа в dotimes:
              {function,dotimes,3,14}.
              {label,13}.
              {func_info,{atom,test},{atom,dotimes},3}.
              {label,14}.
              {test,is_eq_exact,{f,15},[{x,0},{integer,0}]}.
              {move,{x,2},{x,0}}.
              return.
              {label,15}.
              {allocate_zero,2,3}.
              {gc_bif,'-',{f,0},3,[{x,0},{integer,1}],{y,1}}.
              {move,{x,2},{x,4}}.
              {move,{x,1},{x,2}}.
              {move,{x,4},{x,1}}.
              {move,{x,2},{y,0}}.
              {call_fun,2}. < == Вот вызов
              {move,{y,0},{x,1}}.
              {move,{x,0},{x,2}}.
              {move,{y,1},{x,0}}.
              {call_last,3,{test,dotimes,3},2}.

              А это функция которая вызывается:

              {function,'-run2/0-fun-1-',2,21}.

              {make_fun2,{test,'-run2/0-fun-0-',3},2,29791076,1}.


              То есть простыми словами — решение с dotimes создает дополнительно 10000 переменных типа функция, которые потом собираются garbage коллектором.

    • 0
      Erlang плохо справляется с математикой, он не для этого предназначен. Так что результат неудивителен.
  • –1
    Советую также опробовать вот эту реализацию питона:
    code.google.com/p/unladen-swallow/
    • –1
      Я ведь не использовал Intel C++ или Java RTS ;) Вообще, изначально я планировал «в лоб» перевести цикл с C++ на доступные мне языки и сравнить
    • 0
      Опробовал, коммент выше.
  • 0
    ебтыть, shootout.alioth.debian.org/ же есть!
  • +2
    $r = 0;
    for my $i (0..9999) {
      for my $j (0..9999) {
    $r = ($r + ($i * $j) % 100) % 47;
      }
    }
    print "answer: $r\n";


    На языке Perl скорость увеличивается на 30%, предлагаю поправить скрипт для чистоты экперимента.
    Также непонятно почему выбрана не самая последняя версия Perl 5.10.0
    У меня скрипт выполнялся 27 секунд
    • 0
      Действительно. Почему на Python автор вынес инициализацию данных в range, а на Perl — нет? В таком случае на каждой итерации не происходит проверки условия.
      У меня perl5.10.0 выполнял скрипт в среднем 60 секунд из 5 запусков. Машина:
      $ uname -a; grep -i mhz /proc/cpuinfo 
      Linux desktop 2.6.27.19-3.2-default #1 SMP 2009-02-25 15:40:44 +0100 i686 i686 i386 GNU/Linux
      cpu MHz		: 3215.441
      cpu MHz		: 3215.441
      

      • –2
        Потому что всегда считал, что вариант с range медленней. Теперь буду знать, что это не так, спасибо :)
    • 0
      Дополню, если добавить вначало прагму use integer (чтобы все вычисления были целочисленными), результат становится — 22 секунды

      Кстати, по поводу руби-скрипта который выполнялся 30 сек а не 200, подозреваю что у человека просто мощный процессор, у меня вот например — core 2 duo 6850, там что на тестовой конфигурации автора топика думаю будет раза в 2-3 дольше выполнятся
      • +7
        ну и ещё на секунду улучшим результат и сделаем его более Perl-way:

        #!/usr/bin/perl
        use integer;
        use Benchmark;
        $t = timeit(1,sub {
            $r = 0;
            for my $i (0..9999) {
              for my $j (0..9999) {
                $r += $i * $j 100;
                $r %= 47;
              }
            }
            print "answer: $r\n";    
        });
                       
        print "time: ",timestr($t);

        • –7
          Действительно, красиво, спасибо.
          Но на такой код уже менять, наверное не буду, т.к. по-моему, это уже «хак» ;)
          • +7
            В каком месте «хак»?
            Так и должна выглядеть программа на языке Perl,
            правда тут с Benchmark'ом, т.е. программа сама замеряет сколько времени этот код выполняется,
            ну от него легко избавиться.

            Просто в тестировании языков программирования без использования их особенностей ключевых я не вижу смысла.

            Опять же — psycho для питона — хак, а тут — pure Perl, никаких библиотек внешних даже нету… используется только стандартная прагма
            • –3
              Для python и perl описал под диаграммой возможности дополнительного ускорения.
              Но, чёрт возьми, ведь в самом начале хотелось без «заточки кода» под каждый конкретный язык, сравнение просто общих операций :)

              ps: а можно ли при использовании «use integer» пользоваться вычислениями с плавающей точкой? или более точно определять типы переменных?
              • +3
                > Но, чёрт возьми, ведь в самом начале хотелось без «заточки кода» под каждый конкретный язык, сравнение просто общих операций :)

                Для общие операции в каждом языке свои общие общепринятые (и даже идиоматические) способы выражения. :)
                • 0
                  1
              • 0
                Типов переменных как таковых в перле нет. Эта прагма отключает вычисления с плавающей точкой в текущем блоке кода, соответственно все вычисления происходят быстрее.

                Прагмы в Perl действуют в пределах блока кода где они объявлены, в любой момент их можно отменить (no integer, например).

                В частности эта — документирована (то есть присутствует в официальной документации) и является фичей языка (коих там много =) ), целью которой является ускорение частей когда где используется ТОЛЬКО целочисленная арифметика.
              • 0
                сама идея пела состоит с том, что это можно сделать разными способами, и оформление код влияет. (:
    • +1
      Мои результаты и 5 копеек :)
      Оригинальный перл скрипт — 42.323s
      вариант что выше но без «my» — 31.186s
      вариант с «my» — 29.767s

      кстати не знал что my такой полезный в этом плане, кстате почему ??
      perl, v5.8.8
      • 0
        Только вот сидел я, тоже это заметил =)
      • –1
        5.8.8 потому что активно на Perl я не разрабатываю, а в системе (gentoo) что после «emerge world» обновилось, то и работает…
      • 0
        Не настолько хорошо знаком с внутренностями интерпретатора, но могу сделать предположение: следить за локальными переменными «проще», чем за глобальными. Как и в случае с Python.
  • 0
    Ruby-пример на моей системе (С2D 2.66) отработал за 21 секунду.

    $ sudo nice -n -9 time ruby 1.rb
    answer: 39
    20.99 real 20.81 user 0.05 sys


    Версия руби:

    $ ruby -v
    ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-darwin9]
    • –1
      Судя по всему, здесь играет роль либо версия (у меня 1.8 — то, что извлеклось из портов gentoo), либо разрядность процессора (у меня всё ещё 32-битная машинка, в отличие от большинства приводящих результаты здесь, увы мне)
  • НЛО прилетело и опубликовало эту надпись здесь
    • –1
      О, за ссылку спасибо, обязательно прочитаю (только вот у меня очередь на прочтение уже который год всё растёт и растёт, но сейчас она — PriorityQueue :) ).

      А по сравнению — ведь в споре рождается истина. Я не претендовал на «нахождение самого лучшего языка», я просто решил замерить кто и как справится с брутфорс-задачей. В процессе общения с читателями код (в скриптовых языках) претерпел некоторые изменения, производительность выросла. Вариант на Java тоже ускорился, в 5 раз относительно начального варианта.
      Интересно, занимательно, даже, как мне кажется, «общеобразовательно полезно» :)
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          И в статье были ошибки и неточности. Многие из них даже исправлены.

          Неужели Вам никогда не приходилось копать воду или измерять сферического коня просто потому, что любопытно?
          • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Я вас от всей души поздравляю, вы протестировали производительность арифметических выражений. Да ещё и на достаточно некачественных примерах кода. Не думаю, что это что-нибудь отражает в реальности.
    P.S. shootout.alioth.debian.org
    • –1
      задачи на арифметические выражения — вполне реальный класс задач. Навскидку — любая статобработка. Навскидку — raytracing… Автор вполне ясно объяснил свои намерения в статье, поэтому я считаю ваш упрёк не состоятелен.

      PS про shootout уже несколько раз писали в обсуждении.
      • –1
        Скажите, вы будете делать raytracing на PHP? Я — нет. Если бы статья была озаглавлена «производительность математики в реализациях разных ЯП», я был бы согласен с автором. А так — это просто фигня на постном масле, непонятно для кого и непонятно что выражающая. Даже несмотря на пояснение в тексте статьи. Типа да, круто, C++ быстрее сделает рейтрейсинг, чем Python, ну и в чём смысл всего этого?
        • 0
          пузомерки (benchmarks) дают возможность представлять не только «кто шустрее», но «насколько шустрее».

          К примеру у вас есть питоновская программа, которая реализует какую-то логику. Надо добавить числодробительный модуль. Можно написать на питоне, можно сделать вставку на C. Имеет смысл возиться с С? не имея представления какой может быть выигрыш — не ответишь.

        • –1
          Очень неприятно читать такое нытьё, выше писали подобные комменты в стиле «я негодую» уже несколько человек и автор уже несколько раз отвечал. Может хватит?
          • 0
            No offence, но где нытьё? Просто я считаю, что полезность статьи стремится к нулю, вот и всё.
        • –1
          Незадолго перед тестом (может с неделю тому) я видел человека, всерьёз разрабатывающего OCR на PHP. Примеров неэффективного применения языков как средств разработки можно найти достаточно, и некоторые из них сделаны не для забавы, увы
  • 0
    PHP кстати прогрессирует.
    У меня под Windows версия 5.2.6 выполняла код 32 секунды, версия 5.2.10 — 28 секунд, 5.3.0 — 24 секунды.
    Правда 5.2.6 и 5.2.10 были скомпилированы VC6, а 5.3.0 — VC9, может еще и этим разница объясняется.
  • +1
    Чес слово, как будто письками меритесь…
  • +2
    Я смотрю тут перловики со стажем :)
    C-style тут не при чем, сейчас докажу :) Он конечно медленнее range, но не трагически.
    Этот кусок перлкода можно ускорить раза в полтора, тупо объявив все переменные локальными.
    Не забывайте use strict; Он конечно жрет часть памяти и производительности, но это стоит того, эти потери много меньше, чем от пропущенного my ;)
    Ну и использование контексных переменный чуть-чуть ускоряет.
    Про use integer уже сказали, его можно применять контекстно. Да и хак это такой же, как на Java вместо float использовать int ;)

    #~ use strict;
    use Benchmark;

    Benchmark::timethese(1_000, {
    'A' => sub {
    #~ use integer;
    $r = 0;
    for ($i = 0; $i < 100; $i++) {
    for ($j = 0; $j < 100; $j++) {
    $r = ($r + ($i * $j) % 100) % 47;
    }
    }
    },
    'B' => sub {
    #~ use integer;
    my $r = 0;
    for (my $i = 0; $i < 100; $i++) {
    for (my $j = 0; $j < 100; $j++) {
    $r = ($r + ($i * $j) % 100) % 47;
    }
    }
    },
    'C' => sub {
    #~ use integer;
    my $r = 0;
    for my $i (0..99) {
    for my $j (0..99) {
    $r = ($r + ($i * $j) % 100) % 47;
    }
    }
    },
    'D' => sub {
    #~ use integer;
    my $r = 0;
    for my $i (0..99) {
    $r = ($r + ($i * $_) % 100) % 47 for 0..99;
    }
    },
    });

    >perl math.pl
    Benchmark: timing 1000 iterations of A, B, C, D…
    A: 5 wallclock secs ( 5.04 usr + 0.00 sys = 5.04 CPU) @ 198.49/s (n=1000)
    B: 4 wallclock secs ( 3.82 usr + 0.00 sys = 3.82 CPU) @ 261.64/s (n=1000)
    C: 4 wallclock secs ( 3.54 usr + 0.00 sys = 3.54 CPU) @ 282.41/s (n=1000)
    D: 3 wallclock secs ( 3.26 usr + 0.00 sys = 3.26 CPU) @ 306.65/s (n=1000)
    >Exit code: 0 Time: 16.124

    PS: Пардон за форматирование, теги не работают :(
  • –1
    Уже готовые бенчмарки по куче языков и алгоритмов можно посмотреть здесь.
  • +2
    а где же .net? уж очень интересно увидеть насколько он опережает\отстает от остальных участников

    ps: очень удивила разница между php и perl, помню раньше всегда и все говорили что perl гораздо быстрее.
  • 0
    Автор — молодец, что затронул такую тему. То есть, взялся честно изучить различные инструменты. Тема большая, очень важная, и я надеюсь, что автор напишет по ней ещё как минимум одну заметку.

    Что касается сути, то я бы хотел сказать следующее. Тест, выбранный автором, представляет собой два вложенных цикла с постоянными границами. Такие вещи уже давно оптимизируются компиляторами вдоль и поперёк — и в данном случе это не очень хорошо, потому что реальный функционал кода на разных языках на выходе из оптимизатора получается очень разный. Это первое.
    Второе. Этот алгоритм, если хотите, «частный случай», который совсем нечасто возникает в реальной жизни.

    Что же мы используем в реальных программах и как сделать тест ближе к практике? Прежде всего, границы цикла должны быть вычисляемыми (неконстантными) переменными (границы объекта-контейнера). Во-вторых, внутри цикла должно идти не просто вычисление (математика), но и работа с разными переменными. В идеале, тест должен проводиться на контейнерах (!). Потому что реальная производительность в реальных программах характеризуется связкой язык+контейнеры. Скорее всего, имеет смысл даже сделать два теста — с контейнерами и без.
  • 0
    Автору спасибо большое. Если будет продолжение буду очень рад.
    А теперь несколько комментариев-вопросов:
    1) Какая jre использовалась? SunJDK, OpenJDK, java от IBM и т.д. Тоже самое касается и С++ компилера. Подозреваю что это gcc 4.х, но всё же. Ещё нужно бы Intel CC протестировать, поговаривают, что он математику хорошо умеет оптимизировать.
    Да, и почему нет теста с -O3? (или может я просто всё это не заметил это в тексте)
    2) Предлагаю в будущем протестировать ещё код, который активно занимается выделением и очисткой памяти в куче. Думаю, так будет интереснее =)
    • 0
      Версии софта были (по ссылке на старую версию статьи).
      gcc с флагом -O3 и даже -O99 относительно -O2 прироста не дал. Дополнительное -funroll-loops дало около 5% к -O2, что не так весомо.
      На самом деле сейчас хочу собрать всё что разбросано по комментариям и агрегировать. А потом, да, можно будет попробовать работу с массивами, списками, строками, вызовами функций, созданием классов и много ещё с чем попробовать :)
  • 0
    А что за «Ruby ???» такой?
    • 0
      Это руби из комментария habrahabr.ru/blogs/programming/66562/#comment_1873803 (выходит что 1.9)
      • 0
        То есть тесты проходили на разном железе??
        • 0
          Я провёл параллель на моё железо через производительность теста на питоне в той же версии. Так как языки скриптовые, должно было дать относительно верный результат
  • –3
    Идиоты! ну кто сравнивает производительность си-с-перекрестиями и явы, питона и пхп?
  • +1
    Динамические языки предназначены не для высокой производительности. Для этого есть железо.

    Перл надо тестить 5.10? и Руби 1.9.1
  • 0
    Попробуйте прогнать тесты на рекурсивных функциях, например функции Аккермана
    • 0
      Там при m>3 начинаются проблемы со стеком, придётся придумывать алгоритм, а во время рабочего дня думать на посторонние темы — ой :)
      Но на m=3 вполне можно считать до N=13:

      Результаты при многократных запусках достаточно стабильные:

      $ javac Test01.java && time java -Xss8m Test01
      real 0m39.349s
      user 0m37.870s
      sys 0m0.104s

      $ javac Test01.java && time java -Xss8m -server Test01
      real 0m19.630s
      user 0m18.705s
      sys 0m0.068s

      $ g++ Test01.cpp -o Test01 && time ./Test01
      real 1m0.333s
      user 0m57.472s
      sys 0m0.104s

      $ g++ Test01.cpp -O2 -o Test01 && time ./Test01
      real 0m26.549s
      user 0m25.246s
      sys 0m0.048s

      Машинка (на работе):
      $ cat /proc/cpuinfo |fgrep 'model name'
      model name: Intel® Pentium® 4 CPU 3.20GHz
      model name: Intel® Pentium® 4 CPU 3.20GHz

      Код здесь
  • 0
    Почему старая версия Ruby? Текущая current версия 1.9.1, которая как раз отличается другой производительностью.
  • 0
    #include <iostream>
    using namespace std;
    int main(void) {
        long r = 0;
        for (unsigned int i = 0; i < 10000; ++i) {
            for (unsigned int j = 0; j < 10000; ++j) {
                r = (r + (i * j) % 100);
            }
        }
        r %= 47;
        cout << "answer: " << r << endl;
    }
    

    0.21 сек против 0.67 сек эталонного варианта за счет банальной эрудиции
    • 0
      Действительно.

      Однако, на моём unsigned увеличивает время работы в полтора(!) раза. Проверил по 5 запусков.
      На рабочем компе unsigned ускоряет исполнение почти в полтора раза (1.3).

      Похоже, что у меня неправильный ноут, неправильный линукс и неправильный gcc.
      Коплю на новое железо, похоже, пора
    • 0
      За счет вашей «банальной эрудиции» изменился ответ :) Происходит переполнение long'а.

      А вообще не вижу смысла в оптимизации самого алгоритма — это же только синтетический тест. Если бы надо было оптимизировать этот кусок кода, то можно просто написать:

      long r = 39;
      

      :)
      • 0
        Чудо-оптимизация «long r = 39» отличается тем, что от входных параметров(10000, 10000, 100, 47) ответ уже не зависит.
        В 8-байтный long число 4740000000 помещается. Если хотите придираться к переполнению, то есть машины, где произведение 10000*10000 будет не входить в 2-байтовый int.
        • 0
          Я имел в виду, что оптимизация этого кода без привязки к конкретной задаче не имеет смысла.
          А вообще я с вами согласен — алгоритмическая оптимизация рулит :) Но надо при этом быть осторожным, чтобы не изменился результат.
        • 0
          10000, 10000, 100, 47 — это не входные параметры. Это константы в программе. Поэтому строго говоря оптимизатор имеет полное право сделать сверку выражений и достаточно умный оптимизатор в итоге будет иметь просто число 39.

  • 0
    Правильный код на Python:

    from time import clock

    def test():
    r = 0
    for i in xrange(0, 10000):
    for j in xrange(0, 10000):
    r = (r + (i * j) % 100) % 47
    return r

    print( «answer: », test(), clock() )

  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Без apc ваш тест php НЕ корректен.
  • 0
    Погоняйте PHP 5.3 + APC, 5.3 значительно быстрее работает чем 5.2.
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Портировал тест на PuireBasic.

    DisableDebugger
    r.i = 0
    t=ElapsedMilliseconds()
    For i = 0 To 10000
    For j = 0 To 10000
    r = (r + (i * j) % 100) % 47
    Next j
    Next i
    e=ElapsedMilliseconds()-t
    MessageRequester("", StrF(e/1000, 3)+" секунд")

    Проверял на компе с процессором близким к «P4-1.8Ггц», т. е. как и автора статьи.
    Тест был выполнен за 3 секунды.
  • 0
    Добавьте, пожалуйста, больше языков на тестовый график. Кто-то может, кроме автора?

    JavaScript (браузер, консоль отдельно)
    и другие

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