while sentencia for ejemplos ciclo bucle c++ c++11 gcc optimization icc

c++ - sentencia - GCC optimiza el bucle for basado en rango fijo como si tuviera una longitud más larga y variable



ciclo for (1)

Como lo describe Richard Biener en mi gcc.gnu.org/bugzilla/show_bug.cgi?id=81719 , el problema parece ser que GCC anterior a la versión 8 no entendió que un campo de una clase o estructura estaba sujeto a las mismas optimizaciones (por ejemplo, conteo de bucle constante) como una variable regular. Por lo tanto, emitiría todo tipo de código sofisticado para realizar un bucle óptimo un número desconocido de veces, incluso cuando se conocía en el momento de la compilación, en el caso de que el contenedor fuera una variable miembro.

De la forma en que lo entiendo, es probable que este error afecte bastante a un código en la naturaleza, por ejemplo, en cualquier lugar en el que una matriz pequeña miembro sea el objeto de un bucle for de C ++ 11.

Gracias a Richard Biener por la pronta resolución (dirigida a GCC 8).

Tengo una serie de estructuras POD y estoy tratando de sumar en un campo. Aquí hay un ejemplo mínimo:

struct Item { int x = 0; int y = 0; }; typedef Item Items[2]; struct ItemArray { Items items; int sum_x1() const; int sum_x2() const; }; int ItemArray::sum_x1() const { int total = 0; for (unsigned ii = 0; ii < 2; ++ii) { total += items[ii].x; } return total; } int ItemArray::sum_x2() const { int total = 0; for (const Item& item : items) { total += item.x; } return total; }

Las dos funciones de suma hacen lo mismo. Clang los compila de forma idéntica. Pero GCC 6 con -O3 en x86_64 no lo hace. Aquí está sum_x1() , se ve bien:

mov eax, DWORD PTR [rdi+8] add eax, DWORD PTR [rdi] ret

Ahora mira sum_x2() :

lea rdx, [rdi+16] lea rcx, [rdi+8] xor eax, eax add eax, DWORD PTR [rdi] cmp rdx, rcx je .L12 lea rcx, [rdi+16] add eax, DWORD PTR [rdi+8] cmp rdx, rcx je .L2 lea rcx, [rdi+24] add eax, DWORD PTR [rdi+16] cmp rdx, rcx je .L2 lea rcx, [rdi+32] add eax, DWORD PTR [rdi+24] cmp rdx, rcx je .L2 lea rcx, [rdi+40] add eax, DWORD PTR [rdi+32] cmp rdx, rcx je .L2 lea rcx, [rdi+48] add eax, DWORD PTR [rdi+40] cmp rdx, rcx je .L2 lea rcx, [rdi+56] add eax, DWORD PTR [rdi+48] cmp rdx, rcx je .L2 lea rcx, [rdi+64] add eax, DWORD PTR [rdi+56] cmp rdx, rcx je .L2 lea rcx, [rdi+72] add eax, DWORD PTR [rdi+64] cmp rdx, rcx je .L2 add eax, DWORD PTR [rdi+72] ret .L2: rep ret .L12: rep ret

¿Por qué GCC emite un bucle desenrollado de longitud variable de hasta 10 cuando hay una longitud de bucle fija en 2? Solo hace esto en una función miembro, lo que convierte a sum_x2 una función gratuita que lo corrige.

ICC también optimiza sum_x2() muy extrañamente, aunque el código generado es totalmente diferente. A diferencia de GCC, no importa si sum_x2() es una función miembro o una función libre, ambas son malas.

Estoy usando GCC 6, pero todas las versiones de GCC parecen tener problemas con este código. Agregar -march=haswell hace aún peor, agregar iteraciones para hasta 15 elementos en la matriz de tamaño 2. GCC 5 y 7 generan un código aún más complejo, agregando instrucciones SIMD.

Me gustaría identificar la causa exacta de este problema, para poder localizar y corregir incidencias similares en mi código. Comprender qué desencadena este comportamiento en GCC 6 sería muy útil. Tengo muchos bucles basados ​​en rangos en mi código, y no estoy muy entusiasmado con la posibilidad de eliminarlos, pero si GCC no puede generar un código razonable, no tendré otra opción.

Pruébelo: https://godbolt.org/g/9GK4jy

Más locura relacionada: https://godbolt.org/g/BGYggD (el código óptimo es 3 instrucciones; GCC 6 produce 8 instrucciones; GCC 7 produce 130 instrucciones)