Estoy buscando un ejemplo en el que un algoritmo aparentemente está cambiando su clase de complejidad debido a las estrategias de optimización del compilador y / o procesador.
c++
algorithms
optimization
big-o
Lorenz Lo Sauer
fuente
fuente
int main(void) { exit(0); };
Respuestas:
Tomemos un programa simple que imprime el cuadrado de un número ingresado en la línea de comando.
Como puede ver, este es un cálculo de O (n), que se repite una y otra vez.
Compilar esto con
gcc -S
uno obtiene un segmento que es:En esto, puede ver la adición realizada, una comparación y un salto hacia atrás para el ciclo.
Haciendo la compilación
gcc -S -O3
para obtener optimizaciones del segmento entre las llamadas a printf:Ahora se puede ver que no tiene bucle y, además, no se agrega. En cambio, hay una llamada a la
imull
que multiplica el número por sí mismo.El compilador ha reconocido un bucle y el operador matemático dentro y lo ha reemplazado por el cálculo adecuado.
Tenga en cuenta que esto incluyó una llamada a
atoi
para obtener el número. Cuando el número ya existe en el código, el cumplidor calculará previamente el valor en lugar de realizar llamadas reales como se demuestra en una comparación entre el rendimiento de sqrt en C # y C dondesqrt(2)
(una constante) se sumaba en un bucle 1,000,000 de veces.fuente
Tail Call Optimization puede reducir la complejidad del espacio. Por ejemplo, sin TCO, esta implementación recursiva de un
while
bucle tiene una complejidad espacial en el peor de los casosΟ(#iterations)
, mientras que con TCO tiene una complejidad espacial en el peor de los casosΟ(1)
:Esto ni siquiera necesita un TCO general, solo necesita un caso especial muy estrecho, a saber, la eliminación de la recursión directa de la cola.
Sin embargo, lo que sería muy interesante es cuando la optimización del compilador no solo cambia la clase de complejidad sino que también cambia el algoritmo por completo.
El glorioso compilador de Glasgow Haskell a veces hace esto, pero eso no es realmente de lo que estoy hablando, es más como hacer trampa. GHC tiene un lenguaje de coincidencia de patrones simple que permite al desarrollador de la biblioteca detectar algunos patrones de código simples y reemplazarlos con un código diferente. Y la implementación de GHC de la biblioteca estándar de Haskell no contiene algunas de esas anotaciones, para que los usos específicos de las funciones específicas que son conocidos por ser ineficiente se vuelven a escribir en versiones más eficientes.
Sin embargo, estas traducciones están escritas por humanos, y están escritas para casos específicos, por eso considero que hacer trampa.
Un Supercompilador puede cambiar el algoritmo sin intervención humana, pero AFAIK no se ha construido ningún supercompilador a nivel de producción.
fuente
Un compilador que es consciente de que el lenguaje está usando la reducción de fuerza de gran número (reemplazando las multiplicaciones por el índice de un ciclo por una suma) cambiaría la complejidad de esa multiplicación de O (n log n) en el mejor de los casos a O (n) .
fuente