2011-01-25 18 views
6

Tuve un argumento hoy con uno de mis colegas sobre el hecho de que un compilador podría cambiar la semántica de un programa cuando se habilitan optimizaciones agresivas.Puntos de secuencia, condicionales y optimizaciones

Mi colega afirma que cuando las optimizaciones están habilitadas, un compilador puede cambiar el orden de algunas instrucciones. De modo que:

function foo(int a, int b) 
{ 
    if (a > 5) 
    { 
    if (b < 6) 
    { 
     // Do something 
    } 
    } 
} 

podría ser cambiado a:

function foo(int a, int b) 
{ 
    if (b < 6) 
    { 
    if (a > 5) 
    { 
     // Do something 
    } 
    } 
} 

Por supuesto, en este caso, no cambia el programa de comportamiento general y no es realmente importante.

Desde mi entender, creo que los dos pertenecen a dos if (condition) secuencia diferente señala y que el compilador no puede cambiar su orden, incluso si el cambio se mantendría el mismo comportamiento general.

Entonces, queridos usuarios de SO, ¿cuál es la verdad con respecto a esto?

+0

No sé la semántica real sobre los puntos de secuencia, pero solo imagine que ambos casos son equivalentes a 'if (a> 5 && b <6)' ... y que esos son a su vez conmutativos.La única diferencia de rendimiento proviene de lo que es estadísticamente más probable que cause un cortocircuito. Pero la pregunta es, incluso con ese cambio, ¿hay dos puntos de secuencia en cada lado del '&&'? Me imagino que tiene que haber, ya que esa es la única forma en que se pueden implementar los cortocircuitos. –

+2

[Esta respuesta a las preguntas frecuentes] (http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points/4176333#4176333) enumera todos los puntos de secuencia. ¿Lo has visto? – sbi

+0

@sbi: No, yo no. Pero lo haré ahora;) Gracias. – ereOn

Respuesta

12

Si el compilador puede verificar que no hay una diferencia observable entre esos dos, entonces es libre de realizar tales optimizaciones.

Los puntos de secuencia son una cosa conceptual: el compilador tiene que generar un código tal que se comporte como como si se siguieran todas las reglas semánticas como los puntos de secuencia. El código generado en realidad no tiene que seguir esas reglas si no seguirlas no produce una diferencia observable en el comportamiento del programa.

Aún si tiene:

if (a > 5 && b < 6) 

el compilador podría cambiar libremente este ser

if (b < 6 && a > 5) 

porque no hay una diferencia observable entre los dos (en este caso específico en el a y b son ambos valores int). [Esto supone que es seguro leer a y b; si leer uno de ellos podría causar algún error (por ejemplo, uno tiene un valor de trampa), entonces el compilador sería más restringido en las optimizaciones que podría hacer.]

+0

¡Muchas gracias! – ereOn

+0

El compilador no puede reordenar en este caso, hay una garantía con un "y" que la segunda condición NO se evaluará si la primera es falsa, y no creo que importe lo trivial que sea. – CashCow

+5

@CashCow: El compilador puede hacer lo que quiera siempre que no haya una diferencia observable (consulte la cita estándar de C++ dada por Charles Bailey). A veces la reorganización de la expresión causaría una diferencia observable (por ejemplo, 'p! = NULL && p-> x()' es diferente de 'p-> x() && p! = NULL'), pero aquí no hay diferencia. Los puntos de secuencia son conceptuales. –

11

Como no hay observable diferencia entre los dos programas snippets: siempre que la implementación no utilice valores trap o cualquier otra cosa que pueda causar que la comparación interna haga algo más que simplemente evaluar a true o false - el compilador podría optimizar uno a otro bajo la regla "como si" . Si hubiera alguna diferencia observable o alguna forma en que un programa conforme se comportara de manera diferente, entonces el compilador no sería conforme si cambiara una forma por la otra.

Para C++, véase 1.9 [intro.execution]/5.

Una implantación conforme ejecución de un programa bien formada producirá el mismo comportamiento observable como una de las posibles secuencias de ejecución de la correspondiente instancia de la máquina abstracta con el mismo programa y la misma entrada.Sin embargo, si cualquier secuencia de ejecución contiene una operación indefinida , esta Norma Internacional no exige que la implementación ejecute ese programa con esa entrada (ni siquiera con respecto a las operaciones anteriores a la primera operación indefinida).

[Esta disposición a veces se llama la regla de "como si", debido a que una aplicación es libre de hacer caso omiso de cualquier requisito de esta norma, siempre y cuando el resultado es como si el requisito había sido obedecida, en lo se puede determinar a partir del comportamiento observable del programa. Por ejemplo, una implementación real no tiene que evaluar parte de una expresión si se puede deducir que su valor no se utiliza y que no se producen efectos secundarios que afectan al comportamiento observable del programa.]

+0

Gracias Tú mucho. Desearía poder aceptar dos respuestas, pero estoy aceptando la más votada y la mejor para esta. – ereOn

+3

¿Podría eliminar mi voto popular para James y rechazarlo en cambio si eso hace la diferencia? ** ** –

+0

Existen excepciones a la regla de comportamiento observable, como la elisión de los constructores de copia. El compilador puede hacer eso incluso si el comportamiento observable cambia. Y tampoco se requiere que el compilador agregue barreras a la memoria incluso si el reordenamiento de la instrucción lleva al cambio en el comportamiento observable. –

1

Sí, la declaración if es un sequence point.

Sin embargo, un compilador inteligente y agresivo puede reordenar las diferentes expresiones, declaraciones y alterar los puntos de secuencia sin que aparezcan efectos secundarios.

1

Los puntos de secuencia solo se aplican a la máquina abstracta.

Si el optimizador específico de destino puede probar que invertir el orden de dos instrucciones no tiene efectos secundarios, puede cambiarlos a voluntad.

0

El final de una expresión completa (incluidos los que controlan construcciones lógicas como if, while, etcétera) es un punto de secuencia. Sin embargo, el punto de secuencia realmente solo proporciona una garantía de que se han completado los efectos secundarios de las declaraciones previamente evaluadas.

Si una declaración no tiene efectos secundarios observables, el compilador puede hacer lo que considere mejor.

0

La verdad es que si a> 5 es falso con más frecuencia que b < 6 es falso o viceversa, la secuencia hará una pequeña diferencia ya que tendrá que calcular ambos condicionales en más ocasiones.

En realidad, aunque es tan trivial no vale la pena preocuparse en este caso particular.

Hay casos en los que realmente hace una diferencia, es decir, cuando está filtrando una gran colección de datos en varios criterios y tiene que decidir qué filtro aplicar primero, especialmente si solo uno de ellos es O (log N) o constante y los controles posteriores son lineales a través de lo que queda.

0

Porciones de respuestas programador PC =)

El compilador puede, y probablemente sería, optimizar los puntos de secuencia para la velocidad si "b" se pasa a la función en un registro visitada rápidamente, mientras se hace pasar a "a" en la pila. Ese es un caso bastante común para muchos compiladores para MCU de 8 y 16 bits: s.

Mediante la optimización, no es necesario apilar primero "b", luego cargar "a" en un registro, luego evaluar "a", luego cargar "b" en un registro, luego evaluar "b". Todo un lío. Preferiría que el compilador manejara reorganizando los puntos de la secuencia.

Aunque, por supuesto, como ya se mencionó, para ser estándar, el compilador debe asegurarse de que no se modifique el comportamiento del programa mediante la optimización.

Cuestiones relacionadas