Я пишу множество эквивалентных программ на Java и C ++, чтобы сравнить эти два языка по скорости. Эти программы используют сложные математические вычисления в цикле.
Интересно, что C ++ превосходит Java, когда я использую -O3
, Когда я использую -O2
Java побеждает C ++.
Какую оптимизацию компилятора g ++ я должен использовать, чтобы прийти к выводу о моих сравнениях?
Я знаю, что это не так просто заключить, как кажется, но я хотел бы получить некоторое представление о сравнениях задержки и скорости между Java и C ++.
Интересно, что C ++ превосходит Java, когда я использую -O3. Когда я использую -O2, Java побеждает C ++.
-O3
непременно побьет -O2
в микробенчмарках, но когда вы тестируете более реалистичное приложение (например, движок FIX), вы увидите, что -O2
биения -O3
с точки зрения производительности.
Насколько я знаю, -O3
делает очень хорошую работу по компиляции маленький и математический фрагменты кода, но для более реалистичных и больших приложений это может быть медленнее, чем -O2
, Пытаясь агрессивно оптимизировать все (т. Е. Встраивание, векторизацию и т. Д.), Компилятор будет генерировать огромные двоичные файлы, приводящие к отсутствию кэша процессора (т.е. особенно к кэшу инструкций). Это одна из причин, почему Hotspot JIT предпочитает не оптимизировать большие методы и / или не горячие методы.
Важно отметить, что JIT использует методы как независимые единицы, подходящие для оптимизации. В ваши предыдущие вопросы, у вас есть следующий код:
int iterations = stoi(argv[1]);
int load = stoi(argv[2]);
long long x = 0;
for(int i = 0; i < iterations; i++) {
long start = get_nano_ts(); // START clock
for(int j = 0; j < load; j++) {
if (i % 4 == 0) {
x += (i % 4) * (i % 8);
} else {
x -= (i % 16) * (i % 32);
}
}
long end = get_nano_ts(); // STOP clock
// (omitted for clarity)
}
cout << "My result: " << x << endl;
Но этот код JIT-недружественный так как горячий блок кода не в своем собственном методе. Для получения больших преимуществ JIT вы должны были поместить блок кода внутри цикла в его собственный метод. Ваш метод выполняет горячий блок кода вместо горячего метода. Метод, который содержит for
цикл, вероятно, вызывается только один раз, поэтому JIT ничего с этим не сделает.
При сравнении Java с C ++ по скорости следует ли компилировать код C ++ с -O3 или -O2?
Ну, если вы используете -O3
для микробенчмарков вы получите потрясающие быстрые результаты, которые будут нереальными для больших и более сложных приложений. Вот почему я думаю, что судьи использование -O2
вместо -O3
, Например, наш движок Java FIX без мусора быстрее, чем C ++ FIX движки, и я понятия не имею, если они компилируются с -O0
, -O1
, -O2
, -O3
или их сочетание через исполняемые ссылки.
Теоретически, человек может выборочно разделить все приложение на C ++ на исполняемые части, выбрать, какие из них будут скомпилированы -O2
и какие из них будут скомпилированы с -O3
, Затем свяжите все в идеальный двоичный исполняемый файл. Но на самом деле, насколько это возможно?
Подход, который выбирает Hotspot, намного проще. Это говорит:
Послушайте, я собираюсь рассматривать каждый метод как независимую единицу выполнения вместо любого блока кода в любом месте. Если этот метод достаточно горячий (то есть вызывается часто) и достаточно мал, я постараюсь агрессивно его оптимизировать.
Это, конечно, имеет недостаток, требующий прогрев кода но это намного проще и дает лучшие результаты большую часть времени для реалистичных / больших / сложных приложений.
И последнее, но не менее важное: вам следует рассмотреть этот вопрос, если вы хотите скомпилировать все приложение с -O3
: Когда я могу уверенно скомпилировать программу с -O3?
Если возможно, сравните его с обоими, так как -O2 и -O3 — оба варианта, доступные разработчику C ++. Иногда победит -O2. Иногда -O3 победит. Если у вас есть и то, и другое, это просто дополнительная информация, которая может быть использована для поддержки всего, что вы пытаетесь достичь, выполнив эти сравнения скорости.