El programa se ejecuta 3 veces más lento cuando se compila con g ++ 5.3.1 que el mismo programa compilado con g ++ 4.8.4, el mismo comando

El programa se ejecuta 3 veces más lento cuando se compila con g ++ 5.3.1 que el mismo programa compilado con g ++ 4.8.4, el mismo comando


Recientemente, comencé a usar Ubuntu 16.04 con g ++ 5.3.1 y verifiqué que mi programa se ejecuta 3 veces más lento .
Antes de eso, usé Ubuntu 14.04, g ++ 4.8.4.
Lo construí con los mismos comandos:CFLAGS = -std=c++11 -Wall -O3 .


Mi programa contiene ciclos, llenos de llamadas matemáticas (sin, cos, exp).
Puede encontrarlo aquí.


He intentado compilar con diferentes flags de optimización (O0, O1, O2, O3, Ofast), pero en todos los casos se reproduce el problema (con Ofast ambas variantes corren más rápido, pero la primera corre 3 veces más lento aún).


En mi programa uso libtinyxml-dev , libgslcblas . Pero tienen las mismas versiones en ambos casos y no tienen ninguna parte significativa en el programa (según el código y el perfilado de callgrind) en términos de rendimiento.


He realizado perfiles, pero no me da ninguna idea de por qué sucede.
Comparación de Kcachegrind (la izquierda es más lenta).
Solo noté que ahora el programa usa libm-2.23 comparado con libm-2.19 con Ubuntu 14.04.


Mi procesador es i7-5820, Haswell.


No tengo idea de por qué se vuelve más lento. ¿Tienes alguna idea?


PD A continuación puede encontrar la función que consume más tiempo:


void InclinedSum::prepare3D()
{
double buf1, buf2;
double sum_prev1 = 0.0, sum_prev2 = 0.0;
int break_idx1, break_idx2;
int arr_idx;
for(int seg_idx = 0; seg_idx < props->K; seg_idx++)
{
const Point& r = well->segs[seg_idx].r_bhp;
for(int k = 0; k < props->K; k++)
{
arr_idx = seg_idx * props->K + k;
F[arr_idx] = 0.0;
break_idx2 = 0;
for(int m = 1; m <= props->M; m++)
{
break_idx1 = 0;
for(int l = 1; l <= props->L; l++)
{
buf1 = ((cos(M_PI * (double)(m) * well->segs[k].r1.x / props->sizes.x - M_PI * (double)(l) * well->segs[k].r1.z / props->sizes.z) -
cos(M_PI * (double)(m) * well->segs[k].r2.x / props->sizes.x - M_PI * (double)(l) * well->segs[k].r2.z / props->sizes.z)) /
( M_PI * (double)(m) * tan(props->alpha) / props->sizes.x + M_PI * (double)(l) / props->sizes.z ) +
(cos(M_PI * (double)(m) * well->segs[k].r1.x / props->sizes.x + M_PI * (double)(l) * well->segs[k].r1.z / props->sizes.z) -
cos(M_PI * (double)(m) * well->segs[k].r2.x / props->sizes.x + M_PI * (double)(l) * well->segs[k].r2.z / props->sizes.z)) /
( M_PI * (double)(m) * tan(props->alpha) / props->sizes.x - M_PI * (double)(l) / props->sizes.z )
) / 2.0;
buf2 = sqrt((double)(m) * (double)(m) / props->sizes.x / props->sizes.x + (double)(l) * (double)(l) / props->sizes.z / props->sizes.z);
for(int i = -props->I; i <= props->I; i++)
{
F[arr_idx] += buf1 / well->segs[k].length / buf2 *
( exp(-M_PI * buf2 * fabs(r.y - props->r1.y + 2.0 * (double)(i) * props->sizes.y)) -
exp(-M_PI * buf2 * fabs(r.y + props->r1.y + 2.0 * (double)(i) * props->sizes.y)) ) *
sin(M_PI * (double)(m) * r.x / props->sizes.x) *
cos(M_PI * (double)(l) * r.z / props->sizes.z);
}
if( fabs(F[arr_idx] - sum_prev1) > F[arr_idx] * EQUALITY_TOLERANCE )
{
sum_prev1 = F[arr_idx];
break_idx1 = 0;
} else
break_idx1++;
if(break_idx1 > 1)
{
//std::cout << "l=" << l << std::endl;
break;
}
}
if( fabs(F[arr_idx] - sum_prev2) > F[arr_idx] * EQUALITY_TOLERANCE )
{
sum_prev2 = F[arr_idx];
break_idx2 = 0;
} else
break_idx2++;
if(break_idx2 > 1)
{
std::cout << "m=" << m << std::endl;
break;
}
}
}
}
}

Más investigación .
Escribí el siguiente programa simple:


#include <cmath>
#include <iostream>
#include <chrono>
#define CYCLE_NUM 1E+7
using namespace std;
using namespace std::chrono;
int main()
{
double sum = 0.0;
auto t1 = high_resolution_clock::now();
for(int i = 1; i < CYCLE_NUM; i++)
{
sum += sin((double)(i)) / (double)(i);
}
auto t2 = high_resolution_clock::now();
microseconds::rep t = duration_cast<microseconds>(t2-t1).count();
cout << "sum = " << sum << endl;
cout << "time = " << (double)(t) / 1.E+6 << endl;
return 0;
}

Realmente me pregunto por qué este sencillo programa de ejemplo es 2,5 veces más rápido con g++ 4.8.4 libc-2.19 (libm-2.19) que con g++ 5.3.1 libc-2.23 (libm-2.23).


El comando de compilación fue:


g++ -std=c++11 -O3 main.cpp -o sum

El uso de otras banderas de optimización no cambia la proporción.


¿Cómo puedo entender quién, gcc o libc, ralentiza el programa?


Respuestas:


Este es un error en glibc que afecta a las versiones 2.23 (en uso en Ubuntu 16.04) y versiones anteriores de 2.24 (por ejemplo, Fedora y Debian ya incluyen las versiones parcheadas que ya no están afectadas, Ubuntu 16.10 y 17.04 todavía no).


La desaceleración se debe a la penalización de transición de registro SSE a AVX. Consulte el informe de errores de glibc aquí:https://sourceware.org/bugzilla/show_bug.cgi?id=20495


Oleg Strikov escribió un análisis bastante extenso en su informe de errores de Ubuntu:https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1663280


Sin el parche, existen varias soluciones posibles:puede compilar su problema de forma estática (es decir, agregar -static ) o puede deshabilitar el enlace diferido configurando la variable de entorno LD_BIND_NOW durante la ejecución del programa. Una vez más, más detalles en los informes de errores anteriores.


Algunas respuestas de código


void InclinedSum::prepare3D() { double buf1, buf2;
double sum_prev1 = 0.0, sum_prev2 = 0.0;
int break_idx1, break_idx2;
int arr_idx;
for(int seg_idx = 0;
seg_idx <
props->K;
seg_idx++) {
const Point&
r = well->segs[seg_idx].r_bhp;
for(int k = 0;
k <
props->K;
k++)
{
arr_idx = seg_idx * props->K + k;
F[arr_idx] = 0.0;
break_idx2 = 0;
for(int m = 1;
m <= props->M;
m++)
{ break_idx1 = 0;
for(int l = 1;
l <= props->L;
l++) {
buf1 = ((cos(M_PI * (double)(m) * well->segs[k].r1.x / props->sizes.x - M_PI * (double)(l) * well->segs[k].r1.z / props->sizes.z) -
cos(M_PI * (double)(m) * well->segs[k].r2.x / props->sizes.x - M_PI * (double)(l) * well->segs[k].r2.z / props->sizes.z)) / ( M_PI * (double)(m) * tan(props->alpha) / props->sizes.x + M_PI * (double)(l) / props->sizes.z ) +
(cos(M_PI * (double)(m) * well->segs[k].r1.x / props->sizes.x + M_PI * (double)(l) * well->segs[k].r1.z / props->sizes.z) -
cos(M_PI * (double)(m) * well->segs[k].r2.x / props->sizes.x + M_PI * (double)(l) * well->segs[k].r2.z / props->sizes.z)) / ( M_PI * (double)(m) * tan(props->alpha) / props->sizes.x - M_PI * (double)(l) / props->sizes.z )
) / 2.0;
buf2 = sqrt((double)(m) * (double)(m) / props->sizes.x / props->sizes.x + (double)(l) * (double)(l) / props->sizes.z / props->sizes.z);
for(int i = -props->I;
i <= props->I;
i++)
{ F[arr_idx] += buf1 / well->segs[k].length / buf2 * ( exp(-M_PI * buf2 * fabs(r.y - props->r1.y + 2.0 * (double)(i) * props->sizes.y)) - exp(-M_PI * buf2 * fabs(r.y + props->r1.y + 2.0 * (double)(i) * props->sizes.y)) ) * sin(M_PI * (double)(m) * r.x / props->sizes.x) * cos(M_PI * (double)(l) * r.z / props->sizes.z);
}
if( fabs(F[arr_idx] - sum_prev1) >
F[arr_idx] * EQUALITY_TOLERANCE )
{
sum_prev1 = F[arr_idx];
break_idx1 = 0;
} else
break_idx1++;
if(break_idx1 >
1)
{
//std::cout <<
"l=" <<
l <<
std::endl;
break;
} } if( fabs(F[arr_idx] - sum_prev2) >
F[arr_idx] * EQUALITY_TOLERANCE ) {
sum_prev2 = F[arr_idx];
break_idx2 = 0;
} else
break_idx2++;
if(break_idx2 >
1) {
std::cout <<
"m=" <<
m <<
std::endl;
break;
}
}
} } }
#include <cmath>
#include <iostream>
#include <chrono>
#define CYCLE_NUM 1E+7 using namespace std;
using namespace std::chrono;
int main() {
double sum = 0.0;
auto t1 = high_resolution_clock::now();
for(int i = 1;
i <
CYCLE_NUM;
i++)
{
sum += sin((double)(i)) / (double)(i);
}
auto t2 = high_resolution_clock::now();
microseconds::rep t = duration_cast<microseconds>(t2-t1).count();
cout <<
"sum = " <<
sum <<
endl;
cout <<
"time = " <<
(double)(t) / 1.E+6 <<
endl;
return 0;
}
g++ -std=c++11 -O3 main.cpp -o sum 
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libm.a(s_sin.o): in function « __cos »: (.text+0x3542): undefined reference to « _dl_x86_cpu_features » /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libm.a(s_sin.o): in function « __sin »: (.text+0x3572): undefined reference to « _dl_x86_cpu_features »