2010-10-16 20 views
5

editar y refinar mi pregunta después de recibir respuesta valiosa de Johannes¿Es necesario volátil aquí?

bool b = true; 
volatile bool vb = true;  
void f1() { } 
void f2() { b = false; } 

void(* volatile pf)() = &f1; //a volatile pointer to function 

int main() 
{ 
    //different threads start here, some of which may change pf 
    while(b && vb) 
    { 
     pf(); 
    } 
} 

lo tanto, vamos a olvidarnos de sincronización durante un tiempo. La pregunta es si b tiene que declararse volátil. He leído el estándar y conozco la definición formal de semántica volátil (incluso casi los entiendo, la palabra casi es la clave). Pero seamos un poco informales aquí. Si el compilador ve que en el bucle no hay forma de que b cambie, a menos que b sea volátil, puede optimizarlo y asumir que es equivalente a while(vb). La pregunta es, en este caso, que pf es en sí mismo volátil, ¿se le permite al compilador asumir que b no cambiará en el ciclo, incluso si b no es volátil?

Por favor absténgase de los comentarios y respuestas que abordan el estilo de este fragmento de código, este no es un ejemplo del mundo real, esta es una pregunta teórica experimental. Comentarios y respuestas que, además de responder a mi pregunta, también abordan la semántica de la volatilidad en mayor detalle que crees que he malentendido son muy bienvenidos.

Espero que mi pregunta sea clara. TIA

edición, una vez más:
qué pasa con esto?

bool b = true; 
volatile bool vb = true; 
void f1() {} 
void f2() {b = false;} 
void (*pf)() = &f1; 

#include <iosrteam> 
int main() 
{ 
    //threads here 

    while(b && vb) 
    { 
     int x; 
     std::cin >> x; 
     if(x == 0) 
     pf = &f1; 
     else 
     pf = &f2;  
     pf(); 
    } 
} 

Hay una diferencia principal entre los dos programas. Si es así, ¿cuál es la diferencia?

+0

No sé C muy bien. Si este código también es válido C (aparte de bool, que creo que no existe en C), dígame para que agregue la etiqueta C a la pregunta también –

+0

es válida C si #include ybungalobill

+0

primer código su pregunta, y luego pregunte. No es divertido cambiar constantemente la respuesta de uno. Además, ¿qué hilos se permiten cambiar (asumiendo la sincronización adecuada)? ¿Hay mutexes alrededor de ciertas partes? (es decir, alrededor de la parte "assign-pf + call") –

Respuesta

3

La pregunta es, en este caso, que pf es en sí mismo volátil, ¿así el compilador puede suponer que b no cambiará en el bucle incluso si b no es volátil?

No puede, porque usted dice que pf podría ser cambiado por los otros hilos, y esto cambia indirectamente b si pf se llama entonces por el bucle while. Entonces, aunque teóricamente no es obligatorio leer b normalmente, en la práctica debe leerse para determinar si debe producirse un cortocircuito (cuando b se convierte en , no debe leer vb en otro momento).


respuesta a la segunda parte

En este caso pf no es volátil más, por lo que el compilador puede deshacerse de él y ver que f1 tiene un cuerpo vacío y f2 conjuntos b a falso. Se podría optimizar main de la siguiente manera

int main() 
{ 
    // threads here (which you say can only change "vb") 

    while(vb) 
    { 
     int x; 
     std::cin >> x; 
     if(x != 0) 
     break;  
    } 
} 

respuesta a revisión anterior

Una condición para el compilador que se le permitiera optimizar el bucle de distancia es que el bucle no tiene acceso o modificar cualquier volátil objeto (Ver [stmt.iter] p5 en n3126). Usted hace eso aquí, por lo que no puede optimizar el bucle de distancia. En C++ 03 no se permitió que un compilador optimizara incluso la versión no volátil de ese bucle (pero los compiladores lo hicieron de todos modos).

Tenga en cuenta que otra condición para poder optimizarlo es que el bucle no contiene sincronización ni operaciones atómicas. Sin embargo, en un programa multiproceso, debería estar presente de todos modos. Entonces, incluso si se deshace de ese volatile, si su programa está codificado correctamente, no creo que el compilador pueda optimizarlo por completo.

+0

@Johannes: Ok, no puede optimizar el bucle de inmediato, ¿pero está permitido optimizar b lejos y NO leer su valor como la condición de bucle? Es decir. convertirlo en un ciclo infinito? Para evitar decir bucle infinito, digamos que fue while (b && some_other_volatile_bool). ¿El compilador puede convertirlo a while (some_other_volatile_bool)? –

+0

@Armen seguro, si 'b' nunca se cambia, entonces C++ 03 lo permite por completo. Necesita ser volátil para que no se le permita optimizarlo. –

+0

@Johannes: Edité mi pregunta. Además, no puedo decir que esté completamente satisfecho con la respuesta. Usted ve, su respuesta también implica que si un bucle contiene una llamada a través de un puntero a función que se establece dentro del bucle de acuerdo con la entrada del usuario, el compilador puede asumir que b no cambia, lo que no quiero creer es cierto . ¿Debería? –

0

volátil solo lo hiere si cree que podría haberse beneficiado de una optimización que no se puede hacer o si comunica algo que no es cierto.

En su caso, ha dicho que estas variables pueden cambiarse por otros hilos. Código de lectura, esa es mi suposición cuando veo la volatilidad, así que desde la perspectiva de un mantenedor, eso está bien, me está dando información adicional (lo cual es cierto).

No sé si vale la pena intentar rescatar las optimizaciones ya que dijiste que este no es el código real, pero si no lo son, entonces no hay ninguna razón para no usar el volátil.

No se utiliza la volatilidad cuando se supone que se produce un comportamiento incorrecto, ya que las optimizaciones están cambiando el significado del código.

Me preocupo por codificar las minucias del estándar y el comportamiento de sus compiladores porque este tipo de cosas pueden cambiar y, aunque no lo hagan, su código cambia (lo que podría afectar al compilador), así que, a menos que esté buscando para las mejoras de micro-optimización en este código específico, simplemente lo dejaría volátil.

2

Los requisitos exactos en volatile en el estándar actual de C++ en un caso como este son, según entiendo, no del todo definidos por el estándar, ya que el estándar realmente no trata con multi-threading. Básicamente es una pista de compilación. Entonces, en cambio, abordaré lo que sucede en un compilador típico.

En primer lugar, supongamos que el compilador compila sus funciones de forma independiente y luego las une. En cualquier ejemplo, tiene un bucle en el que está comprobando una variable y llamando a un puntero de función. Dentro del contexto de esa función, el compilador no tiene idea de lo que hará la función detrás de ese puntero a la función y, por lo tanto, siempre debe volver a cargar b de la memoria después de llamarlo. Por lo tanto, volatile es irrelevante allí.

expansión, que a su primer caso real, y permitiendo que el compilador para hacer optimizaciones de todo el programa, porque pf es volátil que el compilador todavía tiene ni idea de lo que va a estar apuntando al (ni siquiera puede asumir que es ya sea f1 o f2!), y por lo tanto tampoco se puede hacer ninguna suposición sobre lo que no se modificará a través de la función-puntero de llamada, por lo que volatile en b sigue siendo irrelevante.

Su segundo caso es realmente más simple - vb en él es una pista falsa. Si eliminas eso, puedes ver que incluso en una semántica de un solo subproceso, la llamada al puntero de función puede modificar b. No está haciendo nada con un comportamiento indefinido, por lo que el programa debe funcionar correctamente sin volatile; recuerde que, si no está considerando una situación con ajustes de subproceso externos, volatile no es operativo. Por lo tanto, sin vb en la imagen, no es posible que necesite volatile, y está bastante claro que agregar vb no cambia nada. Por lo tanto, en resumen: No necesita volatile en ninguno de los casos.La diferencia, en la medida en que existe, es que en el primer caso si fp no eran volátiles, un compilador lo suficientemente avanzado podría optimizar b de distancia, mientras que no puede incluso sin volátil en cualquier parte del programa en el segundo caso. En la práctica, no espero que ningún compilador haga esa optimización.

+0

En mi segundo ejemplo, el compilador normalmente entenderá que pf es f1 o f2, analiza ambas funciones y ve que b puede cambiar, o la mera invocación de un puntero a función es suficiente para que se abstenga de optimizando las lecturas de una variable global? –

+0

Y, por cierto, C++ 0x se ocupa de multi-threading ... mucho –

+0

Derecha; Estaba pensando en el estándar actual de C++ (y editado para dejarlo claro). En cualquier caso, me sorprendería si en el segundo ejemplo el compilador hace algún análisis sobre a qué apunta el puntero a la función; No esperaría que, en general, ese sería un análisis productivo (aunque obviamente es útil en este pequeño ejemplo). –