programas programar paso lenguaje ejemplos dev como comandos basicos c++ optimization compiler-optimization goto

programar - ejemplos de programas en c++ pdf



efecto de goto en la optimización del compilador de C++ (4)

Me preguntaba, desde una perspectiva de optimización puramente compiladora, ¿el resultado que los goto tienen en el optimizador del compilador? Hace que el código sea más rápido, más lento o, en general, ningún cambio en el rendimiento en comparación con el uso de temporarios / banderas.

¿Por qué te importa? Su principal preocupación debe ser conseguir que su generador de código cree el código correcto. La eficiencia es de mucha menos importancia que la corrección. Tu pregunta debería ser "¿Mi uso de gotos hará que mi código generado sea más probable o menos probable que sea correcto?"

Mira el código generado por lex / yacc o flex / bison. Ese código está lleno de gotos. Hay una buena razón para eso. Lex y Yacc implementan máquinas de estados finitos. Dado que la máquina pasa a otro estado en las transiciones de estado, el goto es posiblemente la herramienta más natural para tales transiciones.

Hay una forma sencilla de eliminar esos gotos en muchos casos utilizando un bucle while alrededor de una instrucción switch . Este es un código estructurado. Per Douglas Jones (Jones DW, Cómo (no) para codificar una máquina de estados finitos , SIGPLAN No 23, 8 (agosto de 1988), 19-22.), Esta es la peor manera de codificar un FSM. Argumenta que un esquema basado en goto es mejor.

También argumenta que existe un enfoque aún mejor, que es convertir su FSM a un diagrama de flujo de control utilizando técnicas de teoría de grafos. Eso no siempre es fácil. Es un problema difícil de NP. Es por eso que todavía se ven muchos FSM, en particular FSM generados automáticamente, implementados como un bucle alrededor de un conmutador o con transiciones de estado implementadas a través de gotos.

¿Cuáles son los beneficios de rendimiento o las penalizaciones de usar goto con un compilador moderno de C ++?

Estoy escribiendo un generador de código C ++ y el uso de goto facilitará la escritura. Nadie tocará los archivos C ++ resultantes, así que no me pongas "goto is bad" . Como beneficio, ahorran el uso de variables temporales.

Me preguntaba, desde una perspectiva de optimización puramente compiladora, ¿el resultado que goto tiene en el optimizador del compilador? Hace que el código sea más rápido , más lento o, en general, ningún cambio en el rendimiento en comparación con el uso de temporarios / banderas.


¿Cómo crees que están representados los bucles, a nivel de ensamblaje?

Usando instrucciones de salto a etiquetas ...

Muchos compiladores realmente usarán saltos incluso en su Representación Intermedia:

int loop(int* i) { int result = 0; while(*i) { result += *i; } return result; } int jump(int* i) { int result = 0; while (true) { if (not *i) { goto end; } result += *i; } end: return result; }

Rendimientos en LLVM:

define i32 @_Z4loopPi(i32* nocapture %i) nounwind uwtable readonly { %1 = load i32* %i, align 4, !tbaa !0 %2 = icmp eq i32 %1, 0 br i1 %2, label %3, label %.lr.ph..lr.ph.split_crit_edge .lr.ph..lr.ph.split_crit_edge: ; preds = %.lr.ph..lr.ph.split_crit_edge, %0 br label %.lr.ph..lr.ph.split_crit_edge ; <label>:3 ; preds = %0 ret i32 0 } define i32 @_Z4jumpPi(i32* nocapture %i) nounwind uwtable readonly { %1 = load i32* %i, align 4, !tbaa !0 %2 = icmp eq i32 %1, 0 br i1 %2, label %3, label %.lr.ph..lr.ph.split_crit_edge .lr.ph..lr.ph.split_crit_edge: ; preds = %.lr.ph..lr.ph.split_crit_edge, %0 br label %.lr.ph..lr.ph.split_crit_edge ; <label>:3 ; preds = %0 ret i32 0 }

Donde br es la instrucción de rama (un salto condicional).

Todas las optimizaciones se realizan en esta estructura. Por lo tanto, goto es el pan y la mantequilla de los optimizadores.


Estoy de acuerdo con la respuesta de David Hammen, pero solo tengo un punto que agregar.

Cuando se enseña a la gente sobre compiladores, se les enseña sobre todas las maravillosas optimizaciones que pueden hacer los compiladores.

No se les enseña que el valor real de esto depende de quién es el usuario.

Si el código que está escribiendo (o generando) y compilando contiene muy pocas llamadas a funciones y podría consumir una gran fracción del tiempo de algún otro programa, entonces sí, la optimización del compilador es importante.

Si el código que se está generando contiene llamadas a funciones, o si por alguna otra razón el contador del programa pasa una pequeña fracción de su tiempo en el código generado, no vale la pena preocuparse. ¿Por qué? Porque incluso si ese código pudiera optimizarse de manera tan agresiva que tomara tiempo cero , no ahorraría más que esa pequeña fracción, y probablemente haya problemas de rendimiento mucho mayores, que el compilador no puede solucionar, que están contentos de evadir su atención.


La parte de un compilador que se vería afectada trabaja con un gráfico de flujo. La sintaxis que usa para crear un diagrama de flujo particular normalmente será irrelevante; si crea algo como un ciclo while utilizando un goto lugar de una instrucción while real, no afectará en absoluto al optimizador (para ese punto, la sintaxis que produjo el diagrama de flujo se habrá ido hace mucho tiempo).

Sin embargo, es posible producir un gráfico de flujo con goto s que no pueda ser producido por ninguna declaración de control de flujo normal (bucles, interruptor, etc.) En tal caso, puede producir un gráfico de flujo irreductible, y cuando / Si lo hace, a menudo limitará la capacidad del compilador para optimizar el código.

En otras palabras, si (por ejemplo) tomó un código que se escribió con normalidad for , while , switch , etc., y lo convirtió para usar goto en todos los casos, pero conservó la misma estructura, casi cualquier compilador razonablemente moderno probablemente produzca Código esencialmente idéntico de cualquier manera. Sin embargo, si usa goto s para producir el lío de espaguetis como gran parte del FORTRAN que tuve que mirar hace décadas, entonces el compilador probablemente no podrá hacer mucho con él.