У меня есть код, который я запускаю на процессоре Intel Xeon Phi Knights Landing (KNL) 7210 (64 ядра) (это ПК в собственном режиме) и использую компилятор Intel c ++ (icpc) версии 17.0.4. Также я запускаю тот же код на процессоре Intel Core i7, где версия ICPC 17.0.1. Чтобы быть более точным, я компилирую код на машине, которую я запускаю (скомпилирован на i7 и запущен на i7, то же самое для KNL). Я никогда не делаю двоичный файл на одной машине и не переносю его на другую. Циклы распараллелены и векторизованы с использованием OpenMP. Для лучшей производительности я использую флаги компилятора intel:
-DCMAKE_CXX_COMPILER="-march=native -mtune=native -ipo16 -fp-model fast=2 -O3 -qopt-report=5 -mcmodel=large"
На i7 все работает хорошо. Но на KNL код работает без -march=native
и если добавить эту опцию, программа немедленно генерирует исключение с плавающей запятой. Если скомпилировать с единственным флагом «-march = native», то ситуация такая же. Если использовать GDB, он указывает на строку pp+=alpha/rd
часть кода:
...
the code above is run in 1 thread
double K1=0.0, P=0.0;
#pragma omp parallel for reduction(+:P_x,P_y,P_z, K1,P)
for(int i=0; i<N; ++i)
{
P_x+=p[i].vx*p[i].m;
P_y+=p[i].vy*p[i].m;
P_z+=p[i].vz*p[i].m;
K1+=p[i].vx*p[i].vx+p[i].vy*p[i].vy+p[i].vz*p[i].vz;
float pp=0.0;
#pragma simd reduction(+:pp)
for(int j=0; j<N; ++j) if(i!=j)
{
float rd=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)+(p[i].z-p[j].z)*(p[i].z-p[j].z));
pp+=alpha/rd;
}
P+=pp;
}
...
частица p[N];
— массив частиц, Particle — это структура поплавков. N — максимальное количество частиц.
Если убрать флаг -march=native
или заменить его на -march=knl
или с -march=core-avx2
все работает хорошо. Этот флаг делает что-то плохое для программы, но что — я не знаю.
Я нашел в интернете (https://software.intel.com/en-us/articles/porting-applications-from-knights-corner-to-knights-landing, https://math-linux.com/linux/tip-of-the-day/article/intel-compilation-for-mic-architecture-knl-knights-landing) что нужно использовать флаги: -xMIC-AVX512
, Я пытался использовать этот флаг и -axMIC-AVX512
, но они дают ту же ошибку.
Итак, что я хотел спросить:
Зачем -march=native
, -xMIC-AVX512
не работают и -march=knl
работает; является -xMIC-AVX512
включен в -march=native
флаг для КНЛ?
Могу ли я заменить флаг -march=native
с -march=knl
когда я запускаю код на KNL (на i7 все работает), они эквивалентны?
Является ли набор флагов оптимальным для наилучшей производительности при использовании компилятора Intel?
Как сказал Питер Кордес, я разместил здесь вывод ассемблера, когда программа выдает исключение с плавающей точкой в GDB:
1) вывод (gdb) disas:
Program received signal SIGFPE, Arithmetic exception.
0x000000000040e3cc in randomizeBodies() ()
Missing separate debuginfos, use: debuginfo-install libgcc-4.8.5-
16.el7.x86_64 libstdc++-4.8.5-16.el7.x86_64
(gdb) disas
Dump of assembler code for function _Z15randomizeBodiesv:
0x000000000040da70 <+0>: push %rbp
0x000000000040da71 <+1>: mov %rsp,%rbp
0x000000000040da74 <+4>: and $0xffffffffffffffc0,%rsp
0x000000000040da78 <+8>: sub $0x100,%rsp
0x000000000040da7f <+15>: vpxor %xmm0,%xmm0,%xmm0
0x000000000040da83 <+19>: vmovups %xmm0,(%rsp)
0x000000000040da88 <+24>: vxorpd %xmm5,%xmm5,%xmm5
0x000000000040da8c <+28>: vmovq %xmm0,0x10(%rsp)
0x000000000040da92 <+34>: mov $0x77359400,%ecx
0x000000000040da97 <+39>: xor %eax,%eax
0x000000000040da99 <+41>: movabs $0x5deece66d,%rdx
0x000000000040daa3 <+51>: mov %ecx,%ecx
0x000000000040daa5 <+53>: imul %rdx,%rcx
0x000000000040daa9 <+57>: add $0xb,%rcx
0x000000000040daad <+61>: mov %ecx,0x9a3b00(,%rax,8)
0x000000000040dab4 <+68>: mov %ecx,%esi
0x000000000040dab6 <+70>: imul %rdx,%rsi
0x000000000040daba <+74>: add $0xb,%rsi
0x000000000040dabe <+78>: mov %esi,0x9e3d00(,%rax,8)
0x000000000040dac5 <+85>: mov %esi,%edi
0x000000000040dac7 <+87>: imul %rdx,%rdi
0x000000000040dacb <+91>: add $0xb,%rdi
0x000000000040dacf <+95>: mov %edi,0xa23f00(,%rax,8)
0x000000000040dad6 <+102>: mov %edi,%r8d
0x000000000040dad9 <+105>: imul %rdx,%r8
0x000000000040dadd <+109>: add $0xb,%r8
0x000000000040dae1 <+113>: mov %r8d,0xa64100(,%rax,8)
0x000000000040dae9 <+121>: mov %r8d,%r9d
0x000000000040daec <+124>: imul %rdx,%r9
0x000000000040daf0 <+128>: add $0xb,%r9
0x000000000040daf4 <+132>: mov %r9d,0xaa4300(,%rax,8)
0x000000000040dafc <+140>: mov %r9d,%r10d
0x000000000040daff <+143>: imul %rdx,%r10
0x000000000040db03 <+147>: add $0xb,%r10
0x000000000040db07 <+151>: mov %r10d,0x9a3b04(,%rax,8)
0x000000000040db0f <+159>: mov %r10d,%r11d
0x000000000040db12 <+162>: imul %rdx,%r11
0x000000000040db16 <+166>: add $0xb,%r11
0x000000000040db1a <+170>: mov %r11d,0x9e3d04(,%rax,8)
0x000000000040db22 <+178>: mov %r11d,%ecx
0x000000000040db25 <+181>: imul %rdx,%rcx
0x000000000040db29 <+185>: add $0xb,%rcx
0x000000000040db2d <+189>: mov %ecx,0xa23f04(,%rax,8)
2) вывод p $ mxcsr:
(gdb) p $mxcsr
1 = [ ZE PE DAZ DM PM FZ ]
3) вывод p $ ymm0.v8_float:
$2 = {3, 3, 3, 3, 3, 3, 3, 3}
4) вывод p $ zmm0.v16_float:
gdb) p $zmm0.v16_float
$3 = {3 <repeats 16 times>}.
Я также должен упомянуть, что для обнаружения исключений с плавающей запятой я использовал стандарт
void handler(int sig)
{
printf("Floating Point Exception\n");
exit(0);
}
...
int main(int argc, char **argv)
{
feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
signal(SIGFPE, handler);
...
}
Я должен подчеркнуть, что я уже использовал feenableexcept, когда я получил эту ошибку. Я использовал его с начала отладки программы, потому что у нас были ошибки (исключения с плавающей запятой) в коде, и мы должны были их исправить.
Вы использовали feenableexcept
чтобы снять маски с некоторых исключений FP, поэтому оптимизации, создающие недопустимые временные результаты, приведут к сбою вашей программы.
Компилятор Intel с -fp-model fast=2
, лайк gcc -ffast-math
, предполагает, что исключения FP замаскированы, так что это может вызвать FE_INVALID
в некоторых SIMD-элементах в некоторых временных вычислениях, если все в итоге получится (например, смешать, чтобы исправить элементы, в которых задан ошибочный результат). Я предполагаю, что это то, что здесь происходит.
Если вы публикуете разборку самой действующей инструкции, которая вызвала ошибку (вместо набора целых чисел, умноженных в самом начале этой функции), мы можем точно определить, какая оптимизация вызвала то, что недопустимо временно, но в целом вам нужно использовать менее агрессивные параметры FP при компиляции сборок, которые включают исключения FP.
В соответствии с Документация Intel:
-fp-model fast[=1|2] or /fp:fast[=1|2]
Семантика исключений с плавающей точкой отключена по умолчанию, и их нельзя включить, потому что вы не можете указать быстро и только вместе в одной компиляции. Чтобы включить семантику исключений, вы должны явно указать другое ключевое слово (подробности см. В описании других ключевых слов).
Вам нужно использовать -fp-model except
если вы хотите, чтобы компилятор учитывал тот факт, что исключения FP являются видимый побочный эффект. Это не по умолчанию.
Если вы собираетесь вызывать функции, которые изменяют среду FP, ISO C говорит, что вы должны использовать #pragma STDC FENV_ACCESS ON
, и что без этого модификации среды FP не являются «значимыми». «В противном случае реализация может предположить, что режимы управления с плавающей точкой всегда являются режимами по умолчанию и что флаги состояния с плавающей точкой никогда не проверяются и не изменяются». Я не уверен, что включение исключений действительно имеет значение. Вероятно, это не важно, если вы делаете это один раз при запуске программы, иначе будет иметь значение, будет ли вычисление выполнено до или после включения исключений.
Аналогично для GCC, -ffast-math
включает в себя -fno-trapping-math
, который обещает компилятору, что инструкции FP не вызовут SIGFPE, просто тихо установят биты закрепления статуса в MXCSR и выдают NaN (недопустимый), + -Infinity (переполнение) или 0.0
(Сгущенный).
Других решений пока нет …