Оптимизация компилятора: g++ медленнее, чем intel
Недавно я приобрел компьютер с двойной загрузкой кода на С++. В окнах я использую компилятор Intel С++ и g++ на linux.
Мои программы состоят в основном из вычислений (алгоритм итерации с фиксированной точкой с численным интегрированием и т.д.).
Мне показалось, что я могу работать с окнами на моем Linux, но до сих пор я этого не делаю: для одного и того же кода программа, скомпилированная с g++, примерно в 2 раза медленнее, чем у компилятора Intel. Из того, что я читал, icc может быть быстрее, может быть, даже до 20-30% прибыли, но я ничего не читал о том, что он в два раза быстрее (и вообще я действительно читал, что оба они должны быть эквивалентными).
Сначала я использовал флаги, которые примерно эквивалентны:
icl/openmp/I "C:\boost_1_61_0" /fast program.cpp
и
g++ -o program program.cpp -std = С++ 11 -fopenmp -O3 -ffast-math
Следуя советам из нескольких других тем, я попытался добавить/заменить несколько других флагов, таких как: -funsafe-math-optimizations, -march = native, -fwhole-program, -Ofast и т.д. с небольшим (или отсутствием) усилением производительности.
Действительно ли icc быстрее или я что-то упускаю?
Я новичок в Linux, поэтому не знаю, может, я забыл что-то установить (например, драйвер) или изменить какой-либо вариант в g ++? Я понятия не имею, нормальная ситуация или нет, поэтому я предпочитаю спросить. Тем более, что я предпочитаю использовать linux для кодирования в идеале, поэтому я предпочел бы, чтобы это было быстро.
EDIT: Я решил установить последний Linux-компилятор (Intel Compiler С++ 17, update4) на linux для проверки. Я получаю смягчаемые результаты: он НЕ делает лучше, чем gcc (на самом деле хуже).
Я выполнил перекрестное сравнение linux/windows - icc/gcc - распараллеливал или нет, используя флаги, упомянутые ранее в сообщении (чтобы делать прямые сравнения), вот мои результаты (время для запуска 1 итерации, измеренное в мс):
Подводя итог: это немного беспорядок.
В linux gcc, кажется, всегда быстрее, чем icc, особенно когда речь идет о параллелизации (я запускал его для более длительной программы, разница намного выше, чем здесь).
В окнах это противоположное и icc явно доминирует gcc, особенно когда нет распараллеливания (в этом случае gcc занимает очень много времени для компиляции).
Самая быстрая компиляция выполняется с помощью распараллеливания и icc на окнах. Я не понимаю, почему я не могу воспроизвести это на linux. Что мне нужно сделать (ubuntu 16.04), чтобы помочь скрепить мои процессы?
Другое отличие состоит в том, что в Windows я использую более старый Intel-композитор (Composer XE 2013) и вызываю 'ia32' вместо intel64 (который я должен использовать), а на linux я использую последний версию, которую я установил вчера. И на Linux папка Intel Compiler 17 находится на моем втором hdd (а не на моем ssd, на котором устанавливается Linux). Я не знаю, может ли это замедлить работу.
Любая идея, из которой может возникнуть проблема?
Изменить: Точное оборудование:
Intel (R) Core (TM) i7-4710HQ CPU @2.50GHz, 8 CPU, 4 ядра, 2 потока на ядро, архитектура x86_64
- Linux Ubuntu 16.04 с gcc 5.4.1 и Intel Compiler 17 (update4)
- Windows 8.1, Intel Composer 2013
Изменить: Код очень длинный, вот форма цикла, который я тестирую (т.е. только одна итерация моей итерации с фиксированной точкой). Это очень классический, я думаю... не уверен, что он может принести что-нибудь в тему.
// initialization of all the objects...
// length_grid1 is about 2000
vector< double > V_NEXT(length_grid1), PRICE_NEXT(length_grid1);
double V_min, price_min;
#pragma omp parallel
{
#pragma omp for private(V_min, price_min, i, indexcurrent, alpha, beta)
for (i = 0; i < length_grid1; i++) {
indexcurrent = indexsum[i];
V_min = V_compute(&price_min, indexcurrent, ...);
V_NEXT[indexcurrent] = V_min; PRICE_NEXT[indexcurrent] = price_min;
}
}// end parallel
где функция V_compute - это классический и простой алгоритм оптимизации (настраиваемый золотой поиск), возвращающий оптимальное значение и его аргумент:
double V_compute(double *xmin, int row_index, ... ) {
double x1, x2, f1, f2, fxmin;
// golden_ratio=0.61803399;
x1 = upper_bound - golden_ratio*(upper_bound - lower_bound);
x2 = lower_bound + golden_ratio*(upper_bound - lower_bound);
// Evaluate the function at the test points
f1 = intra_value(x1, row_index, ...);
f2 = intra_value(x2, row_index, ...);
while (fabs(upper_bound - lower_bound) > tolerance) {
if (f2 > f1){
upper_bound = x2; x2 = x1; f2 = f1;
x1 = upper_bound - golden_ratio*(upper_bound - lower_bound);
f1 = intra_value(x1, row_index, ...);
} else {
lower_bound = x1; x1 = x2; f1 = f2;
x2 = lower_bound + golden_ratio*(upper_bound - lower_bound);
f2 = intra_value(x2, row_index, ...);
}
}
// Estimated minimizer = (lower bound + upper bound) / 2
*xmin = (lower_bound + upper_bound)/2;
fxmin = intra_value(*xmin, row_index, ...);
return - fxmin; }
Оптимизированная функция (intra_value) довольно сложна с точки зрения вычисления (выберите точку сетки (row_index) из предварительно скомпилированной сетки, затем включите много числового интегрирования и т.д.).
Ответы
Ответ 1
Похоже, вы используете OpenMP, поэтому я подозреваю, что разница заключается в реализации OpenMP, а не только в качестве оптимизированного кода.
Время работы в Intel OpenMP, как известно, довольно высокая, а GCC - хороший, но не большой.
Программы OpenMP имеют очень разные характеристики производительности, они не просто зависят от того, насколько хорошо компилятор может оптимизировать циклы или встроенные вызовы функций. Реализация среды OpenMP имеет большое значение, а также реализация ОС потоков и примитивов синхронизации, которые совершенно разные между Windows и GNU/Linux.
Ответ 2
Обратите внимание, что "fast-math" нарушает некоторые языковые правила, чтобы получить быстрый код и может привести к некорректным результатам в некоторых случаях.
Также обратите внимание, что -O3
не гарантируется быстрее, чем -O2
или любой из других уровней оптимизации (это зависит от вашего кода) - вы должны протестировать несколько версий.
Вы также можете включить -Wl,-O1
- компоновщик также может сделать некоторые оптимизации.
Вы также можете попытаться построить с помощью LTO (оптимизация времени соединения) - он может часто давать значительные улучшения.
Я понимаю, что это не отвечает на ваш вопрос как таковой. Но он должен дать вам некоторые вещи, чтобы играть с: -)
Кроме того, gcc улучшается довольно быстро. Вы можете попробовать более новую версию, если ее еще нет в 7.1. Также; попробуйте Clang для третьего datapoint. Кроме того, вы можете использовать icc для Linux, если хотите.