while una raiz potencia para numero hacer for dev cuadrada con codigo ciclo c++ c optimization gcc

una - raiz cuadrada en c++



¿El compilador de ac/c++ optimiza las divisiones constantes por el valor de la potencia de dos en turnos? (4)

Incluso con g++ -O0 (sí, -O0 !), Esto sucede. Su función se compila a:

_Z3divm: .LFB952: pushq %rbp .LCFI0: movq %rsp, %rbp .LCFI1: movq %rdi, -24(%rbp) movq $64, -8(%rbp) movq -24(%rbp), %rax shrq $6, %rax leave ret

Tenga en cuenta que shrq $6 , que es un cambio a la derecha en 6 lugares.

Con -O1 , se -O1 la basura innecesaria:

_Z3divm: .LFB1023: movq %rdi, %rax shrq $6, %rax ret

Resultados en g ++ 4.3.3, x64.

La pregunta lo dice todo. ¿Alguien sabe si lo siguiente ...

size_t div(size_t value) { const size_t x = 64; return value / x; }

... está optimizado en?

size_t div(size_t value) { return value >> 6; }

¿Los compiladores hacen esto? (Mi interés radica en GCC). ¿Hay situaciones donde lo hace y otras donde no?

Realmente me gustaría saber, porque cada vez que escribo una división que podría optimizarse así, gasto algo de energía mental preguntándome si no se desperdicia nada precioso haciendo una división en la que un cambio sería suficiente.


La mayoría de los compiladores irán más allá de reducir la división por potencias de 2 en turnos: a menudo convertirán la división de enteros por una constante en una serie de instrucciones de multiplicación, desplazamiento e incorporación para obtener el resultado en lugar de usar la división integrada de la CPU instrucción (si es que hay una)

Por ejemplo, MSVC convierte la división por 71 a lo siguiente:

// volatile int y = x / 71; 8b 0c 24 mov ecx, DWORD PTR _x$[esp+8] ; load x into ecx b8 49 b4 c2 e6 mov eax, -423447479 ; magic happens starting here... f7 e9 imul ecx ; edx:eax = x * 0xe6c2b449 03 d1 add edx, ecx ; edx = x + edx c1 fa 06 sar edx, 6 ; edx >>= 6 (with sign fill) 8b c2 mov eax, edx ; eax = edx c1 e8 1f shr eax, 31 ; eax >>= 31 (no sign fill) 03 c2 add eax, edx ; eax += edx 89 04 24 mov DWORD PTR _y$[esp+8], eax

Entonces, obtienes una división por 71 con un multiplicador, un par de turnos y un par de puntos.

Para obtener más detalles sobre lo que está sucediendo, consulte el libro "Hacker''s Delight" de Henry Warren o la página web complementaria:

Hay un capítulo agregado en línea que proporciona información adicional acerca de la división por constantes usando la multiplicación / desplazamiento / adición con números mágicos, y una página con un pequeño programa de JavaScript que calculará los números mágicos que necesita.

Merece la pena leer el sitio complementario del libro (como es el libro), especialmente si le interesan las micro optimizaciones a nivel de bit.

Otro artículo que descubrí hace un momento que analiza esta optimización: http://blogs.msdn.com/devdev/archive/2005/12/12/502980.aspx


Sí, los compiladores generan el código más óptimo para tales cálculos simplistas. Sin embargo, no entiendo por qué insiste específicamente en "turnos". El código óptimo para una plataforma dada podría ser algo diferente de un "cambio".

En general, la idea antigua y golpeada hasta la muerte de que un "cambio" es de algún modo la forma más óptima de implementar multiplicaciones y divisiones de potencia de dos tiene muy poca relevancia práctica en las plataformas modernas. Es una buena forma de ilustrar el concepto de "optimización" para los novatos, pero no más que eso.

Su ejemplo original no es realmente representativo, porque utiliza un tipo sin signo, lo que simplifica enormemente la implementación de la operación de división. El requisito de "redondear hacia cero" de los lenguajes C y C ++ hace que sea imposible hacer una división con un simple desplazamiento si el operando está firmado.


Solo cuando puede determinar que el argumento es positivo. Ese es el caso para su ejemplo, pero desde que C99 especificó la semántica de redondeo a cero para la división de enteros, se ha vuelto más difícil optimizar la división por potencias de dos en turnos, porque dan resultados diferentes para los argumentos negativos.

En reacción al comentario de Michael a continuación, aquí hay una forma en que la división r=x/p; de x por una potencia conocida de dos p puede ser traducida por el compilador:

if (x<0) x += p-1; r = x >> (log2 p);

Dado que OP estaba preguntando si debería pensar en estas cosas, una respuesta posible sería "solo si conoce mejor el signo del dividendo que el compilador o sabe que no importa si el resultado se redondea hacia 0 o -∞".