юмор

Ну, апологеты Си, Гитлер вам капут!

Щас точки над Ё расставлять буду.

Как правильно измерять производительность программы, написанной на каком-то языке программирования? Ну, для начала, взять нормальный, хорошо изученный алгоритм. Например, любимая многими сортировка пузырьком (bubble sort) не годится. Почему? Потому что это настолько идиотский алгоритм, что он сбивает с толку предсказатель ветвления у процессора. Происходит это из-за того, что невозможно предугадать, как дальше пойдёт выполнение алгоритма, из-за чего самый худший (на бумаге) случай для сортировки пузырьком -- когда исходный массив уже рассортирован, но в обратном порядке -- на деле сортируется быстрее, чем (псевдо)случайный. Таким образом, производительность этого алгоритма в большей степени зависит от того, как работает само железо, а не насколько производителен алгоритм, написанный на каком-то языке.

А брать надо, например, сортировку вставками (insertion sort). Там всё более-менее внятно.

Далее берём и пишем программы на разных языках, стараясь делать как можно меньше различий (хотя данная философия летит под откос, если надо сравнить, например, Джаву и Хаскелл -- там вообще парадигма программирования разная). Для большинства императивных языков -- сработает. Компилируем в бинарники, запускаем, и смотрим, кто дольше выполняется.

Вот и давайте проделаем сей фокус. Я нарисовал сортировку вставками на Джаве и на голом Си. Происходит сортировка массива с 100 000 элементами, чьи значения варьируются от 0 до 32768.

Код прячу в спойлеры.

[Джава]
import java.util.Random;

public class insertionsort
{
public static void main(String[] args)
{
int taskSize = 100000;
int [] array = new int[taskSize];
Random randomizer = new Random();
int intermediate;

for (int i = 0; i < taskSize; i++)
{
array[i] = randomizer.nextInt(32768);
}

long startTime = System.nanoTime();

for (int i = 0; i < taskSize; i++)
{
for (int j = 0; j < taskSize; j++)
{
if (array[j] > array[i])
{
intermediate = array[j];
array[j] = array[i];
array[i] = intermediate;
}
}
}

long endTime = System.nanoTime();

System.out.println("Elapsed time, seconds: " + (endTime - startTime) / 1000000000.0);

}

}


[Си]
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
int taskSize = 100000;

int array[taskSize];

int intermediate = 0;

for (int i = 0; i < taskSize; i++)
{
array[i] = rand() % 32768;

}

clock_t startTime = clock();

for (int i = 0; i < taskSize; i++)
{
for (int j = 0; j < taskSize; j++)
{
if (array[j] > array[i])
{
intermediate = array[j];
array[j] = array[i];
array[i] = intermediate;
}
}
}

clock_t endTime = clock();

double elapsed_time = (double)(endTime - startTime) / CLOCKS_PER_SEC;

printf("Elapsed time, seconds: %f\n", elapsed_time);

}


Как видите, ничего экстраординарного, обычный алгоритм сортировки с O(n2) сложностью.

Сишный код компилировал, разумеется, clang, со степенью оптимизации 3 (i.e. clang sorter.c -o sorter-clang -O3). Джаву компилировал в jar из Эклипса, JVM 9. Всё запускалось в виртуалке под Убунту 16.

И что получилось?

Программа на Си, скомпилированная clang, выполняется в среднем 11.57 секунд (min. 11.56, max. 11.58)

А та же программа на Джаве выполняется... та-дам!! -- 10.43 секунды (min. 10.42, max 10.46), то-есть, быстрее, чем на одну секунду. Получается, что Джава работает быстрее Си на 11% -- на данном алгоритме.

Так что извините, граждане, верующие, что Программа, Переписанная На Си, Всегда Будет Быстрее -- это не так.

ОК, может, это clang такой плохой компилятор? Извольте, давайте попробуем gcc (gcc sorter.c -o sorter-gcc -O3). Получилось ещё хуже =)))

Рабочее время программы стало составлять 16 секунд в среднем (min. 15.95, max. 16.06). Очевидно, потому, что gcc совсем говённый компилятор (GNU-тый софт, блин).

Предвижу, что щас налетят Настоящие Программисты на Си и скажут, что Сишный код надо было писать с Специальными ч0рными Заклинаниями, например, с указателями, и передвигаться по массиву не через цикл for, а через инкремент указателя. На что я возражу, что это получится уже немного другая программа и сравнивать их будет некорректно. И что тут уже не язык программирования будет оказывать влияние, а сама программа, так что сравнение будет бессмысленным.
Вот тут уже получилось заметно хуже Джавы. В среднем получилось 17.75 секунд (min. 17.42 max. 18.25). Причём это не на виртуалке, а на материнской ОС Windows. Надо будет попробовать под Mono её на Убунте зафигарить, аж самому стало интересно.
Под Mono на виртуалке получилось 18.4 секунды в среднем. Ожидаемое ухудшение. Даже удивлён, что не получилось хуже -- оказывается, Mono не так уж и плоха.
Попробуйте в качестве чОрной магии использовать thread-local memory management вместо глобальных malloc/free, разница должна быть весьма ощутимой. Disclaimer- для меня это аргумент в пользу Java, где можно обойтись без бубна.
Дак я и не использую malloc/free, сразу создаю всё нужного размера.
Да, я не посмотрел сразу на код, и мой комментарий скорее относился к "общему случаю". В данном было бы интересно посмотреть, как С++ vs Java используют виртуальную память, и, конкретно, swap. Запросто может оказаться, что JVM держит все страницы в памяти, а С++ передает весь контроль в ОС, которая может с ними делать, что ей вздумается
Попробовал поглядеть -- в целом, конечно, Джава отжирает довольно много, там сама jvm занимает внушительно места. Сишная же программа отъедает мало -- там ведь только массив с 100 000 элементами, по 4 байта на элемент получается ~400 килобайт. Примерно столько оно и отъело (сколько отъела Джава щас навскидку не скажу, но минимум раза в три больше).

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

По теме: я так понимаю время что jvm заводилась включено в эти 10 секунд?
Не включено, конечно, я же измерял не скорость запуска программы, а скорость самого, собственно, выполнения алгоритма сортировки вставками. К тому же, скорость включения виртуальной машины полюбасу будет O(1).

Что до наезда по поводу "джавистов плющит" -- не джависты начали эти наезды, вообще-то ;-)

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

Просто подзадолбало уже -- "да на плюсах то же самое будет работать на два порядка быстрее!!!!" Авотхер, не будет -- вот, пример выше.

Хочется попробовать на другом алгоритме? Ну давай попробуем, предлагай алгоритм. Может быть, я даже судоку свою на сях перепишу, поглядим.

И чем это, интересно, запуск в виртуалке тебе не угодил? Чуть ли не пол-интернета сидит в облаке, оно там как, по-твоему, на голом железе всё запускается?