Я работал над оптимизацией производительности и, конечно, проводил регрессионные тесты, когда заметил, что g ++, похоже, меняет результаты в зависимости от выбранной оптимизации. До сих пор я думал, что -O2 -march=[whatever]
должны давать одинаковые результаты для численных расчетов независимо от того, какая архитектура выбрана. Однако, похоже, это не относится к g ++. Хотя использование старых архитектур вплоть до ivybridge дает те же результаты, что и clang для любой архитектуры, я получаю разные результаты для gcc для haswell и новее. Это ошибка в gcc или я что-то не так понял по поводу оптимизации? Я действительно поражен, потому что Clang, кажется, не показывает такое поведение.
Обратите внимание, что я хорошо знаю, что различия находятся в пределах точности машины, но они все еще мешают моим простым проверкам регрессии.
Вот пример кода:
#include <iostream>
#include <armadillo>
int main(){
arma::arma_rng::set_seed(3);
arma::sp_cx_mat A = arma::sprandn<arma::sp_cx_mat>(20,20, 0.1);
arma::sp_cx_mat B = A + A.t();
arma::cx_vec eig;
arma::eigs_gen(eig, B, 1, "lm", 0.001);
std::cout << "eigenvalue: " << eig << std::endl;
}
Скомпилировано с использованием:
g++ -march=[architecture] -std=c++14 -O2 -o test example.cpp -larmadillo
версия gcc: 6.2.1
версия clang: 3.8.0
Скомпилировано для 64 бит, выполнено на процессоре Intel Skylake.
Это потому, что GCC по умолчанию использует инструкцию fused-multiply-add (fma), если она доступна. Clang, напротив, не использует их по умолчанию, даже если это доступно.
Результат от a*b+c
Можно отличаться Используется ли fma или нет, поэтому вы получаете разные результаты, когда вы используете -march=haswell
(Haswell — первый процессор Intel, поддерживающий fma).
Вы можете решить, хотите ли вы использовать эту функцию с -ffp-contract=XXX
,
-ffp-contract=off
, вы не получите FMA инструкции.-ffp-contract=on
Вы получаете инструкции FMA, но только в случае сокращения, если это разрешено стандартом языка. В текущей версии GCC это означает отключение (поскольку оно еще не реализовано).-ffp-contract=fast
(это GCC по умолчанию), вы получите инструкции FMA.Других решений пока нет …