2011-04-05 6 views
10

El C++0x draft tiene una noción de cercas que parece muy distinta de una noción de nivel de CPU/chip de cercas, o decir lo que los tipos de kernel de Linux esperan de fences. La pregunta es si el borrador realmente implica un modelo extremadamente restringido, o la redacción es simplemente pobre y en realidad implica vallas verdaderas.Cercas en C++ 0x, garantías solo en atómicos o memoria en general

Por ejemplo, bajo 29,8 Cercos que afirma cosas como:

Una valla de liberación A se sincroniza con una cerca adquieren B si existen atómicas operaciones X e Y, a la vez funcionando en algunos atómica oponerse M, tal que a es secuenciaron antes de X, X modi fi es H, y es secuenciaron antes de B, e y lee el valor escrito por X o un valor escrito por cualquier lado e ff ect en el hipotético liberación secuencia X haría head si es fueron una operación de lanzamiento.

Utiliza estos términos atomic operations y atomic object. Hay tales operaciones y métodos atómicos definidos en el borrador, pero ¿solo significa eso? Una valla de liberación suena como una valla de la tienda . Una valla de la tienda que no garantiza la escritura de todos los datos antes de la valla es casi inútil. Similar para una valla de carga (adquirir) y una valla completa.

Entonces, ¿las cercas/barries en las cercas adecuadas de C++ 0x y la redacción son increíblemente malas, o están extremadamente restringidas/inútiles como se describe?


En términos de C++, decir que tengo el código existente (vallas suponiendo están disponibles tan alto nivel de construcciones de este momento - en vez de, por ejemplo usando __sync_synchronize en GCC):

Thread A: 
b = 9; 
store_fence(); 
a = 5; 

Thread B: 
if(a == 5) 
{ 
    load_fence(); 
    c = b; 
} 

asumir una, b, c son de un tamaño para tener una copia atómica en la plataforma. Lo anterior significa que c solo se le asignará 9. Tenga en cuenta que no nos importa cuando el hilo B ve a==5, solo que cuando lo hace también ve b==9.

¿Cuál es el código en C++ 0x que garantiza la misma relación?


RESPUESTA: Si usted lee mi respuesta elegida y todos los comentarios que obtendrá la esencia de la situación. C++ 0x parece forzarlo a usar un atómico con vallas, mientras que una valla de hardware normal no tiene este requisito. En muchos casos, esto todavía se puede usar para reemplazar algoritmos concurrentes siempre que sizeof(atomic<T>) == sizeof(T) y atomic<T>.is_lock_free() == true.

Sin embargo, es lamentable que is_lock_free no sea un constexpr. Eso permitiría su uso en un static_assert. Tener atomic<T> degenerado para usar bloqueos generalmente es una mala idea: los algoritmos atómicos que usan mutex tendrán problemas de conflicto terribles en comparación con un algoritmo diseñado por mutex.

+7

El uranio empobrecido se ha desplazado como el material de elección para los proyectiles de artillería. Las copias obsoletas del borrador de C++ 0x resultaron ser más densas. –

+0

Aunque ese solo tiene un mes o algo así. –

+0

Hans podría saber algo que nosotros no? La propuesta ** real ** para C++ 11 será lanzada la próxima semana. –

Respuesta

14

Las cercas proporcionan pidiendo en todos los datos. Sin embargo, para garantizar que la operación de vallas de un hilo sea visible por un segundo, debe usar operaciones atómicas para el indicador, de lo contrario, tendrá una carrera de datos.

std::atomic<bool> ready(false); 
int data=0; 

void thread_1() 
{ 
    data=42; 
    std::atomic_thread_fence(std::memory_order_release); 
    ready.store(true,std::memory_order_relaxed); 
} 

void thread_2() 
{ 
    if(ready.load(std::memory_order_relaxed)) 
    { 
     std::atomic_thread_fence(std::memory_order_acquire); 
     std::cout<<"data="<<data<<std::endl; 
    } 
} 

Si thread_2 lee ready ser true, a continuación, las cercas aseguran que data con seguridad se puede leer, y la salida será data=42. Si se lee que ready es false, entonces no puede garantizar que thread_1 haya emitido la valla apropiada, por lo que una valla en el subproceso 2 no proporcionaría las garantías de pedido necesarias --- si se omitiera el if en thread_2, el acceso a data ser una raza de datos y un comportamiento indefinido, incluso con la valla.

Aclaración: A std::atomic_thread_fence(std::memory_order_release) es generalmente equivalente a una cerca de la tienda, y es probable que se implemente como tal. Sin embargo, una sola valla en un procesador no garantiza ningún orden de memoria: necesita una valla correspondiente en un segundo procesador, Y. Debe saber que cuando se ejecutó la valla de adquisición los efectos de la valla de liberación eran visibles a ese segundo procesador. Es obvio que si la CPU A emite una valla de adquisición, y luego 5 segundos más tarde la CPU B emite una valla de liberación, entonces esa valla de liberación no se puede sincronizar con la valla de adquisición. A menos que tenga algún medio para verificar si la valla se ha emitido en la otra CPU, el código de la CPU A no puede decir si emitió su valla antes o después de la valla en la CPU B.

El requisito que utiliza una operación atómica para comprobar si se ha visto o no la cerca es una consecuencia de las reglas de carrera de datos: no se puede acceder a una variable no atómica desde múltiples hilos sin una relación de ordenamiento, por lo que no se puede usar una variable no atómica para verificar una relación ordenando

Por supuesto, se puede usar un mecanismo más fuerte como un mutex, pero eso haría que la valla separada no tenga sentido, ya que el mutex proporcionaría la valla.

Las operaciones atómicas relajadas son probablemente simples cargas y se almacenan en CPU modernas, aunque posiblemente con requisitos de alineación adicionales para garantizar la atomicidad.

El código escrito para utilizar vallas específicas del procesador se puede cambiar fácilmente para usar vallas C++ 0x, siempre que las operaciones utilizadas para verificar la sincronización (en lugar de las utilizadas para acceder a los datos sincronizados) sean atómicas. El código existente puede basarse en la atomicidad de las cargas simples y almacena en una CPU determinada, pero la conversión a C++ 0x requerirá el uso de operaciones atómicas para esos controles con el fin de proporcionar las garantías de pedido.

+0

Qué parte de la norma aclara que la ordenación está sin embargo en * todos los datos * y no solo en los atómicos. ¿Eso es lo que estoy buscando? El requisito de que tenga que usar un átomo (como lo indica) significa que estas no son vallas adecuadas. –

+1

Las secciones relevantes de la norma son 1.10, 29.3 y 29.8. Las reglas de ordenamiento son complicadas, pero si A * está secuenciada -antes * B en el mismo hilo, y B * sincroniza-con * C en otro hilo, y C está * secuenciada-antes * D en ese segundo hilo, entonces A * sucede antes de * D, y A y D pueden acceder a los mismos datos, incluso si no son operaciones atómicas. Los párrafos dados especifican qué B y C deben ser para proporcionar esta garantía. Si no existe una relación * sucede antes * entre accesos no atómicos, entonces tiene una carrera de datos. –

+0

Eso parece incorrecto entonces (el estándar, no su explicación). Las cercas de hardware no requieren una operación atómica para crear la relación * sucede antes *, la cerca sola debería ser suficiente. –

2

Según tengo entendido, son vallas adecuadas.La evidencia circunstancial es que, después de todo, están destinados a mapear las características que se encuentran en el hardware real y que permite la implementación eficiente de algoritmos de sincronización. Como dices, las vallas que se aplican solo a algunos valores específicos son 1. inútiles y 2. no se encuentran en el hardware actual.

Dicho esto, AFAICS la sección que cita describe la relación "sincroniza con" entre vallas y operaciones atómicas. Para obtener una definición de lo que esto significa, consulte la sección 1.10 Ejecuciones multiproceso y razas de datos. Nuevamente, AFAICS, esto no implica que las vallas se apliquen solo a los objetos atómicos, sino que sospecho que el significado es que mientras las cargas y almacenes comunes pueden pasar, adquiere y suelta cercas de la manera habitual (solo una dirección), cargas atómicas/las tiendas no pueden.

Wrt. objetos atómicos, mi entendimiento es que en todos los objetivos Linux soporta, las variables enteras simples alineadas correctamente cuyo tamaño de() < = sizeof (* void) son atómicas, por lo tanto, Linux usa enteros normales como variables de sincronización (es decir, las operaciones atómicas del kernel de Linux operan en variables enteras normales). C++ no desea imponer tal limitación, de ahí los tipos enteros atómicos separados. Además, en las operaciones de C++, los tipos de enteros atómicos implican barreras, mientras que en el kernel de Linux todas las barreras son explícitas (lo cual es bastante obvio ya que sin el soporte del compilador para los tipos atómicos eso es lo que se debe hacer).

+0

He vuelto a leer la sección 1.10 y no tengo muy claro si las garantías de pedido se aplican a valores no atómicos. Casi todos los puntos indican solo objetos atómicos. El punto 13 menciona no escalares, y los elementos anteriores se relacionan con las relaciones, pero no estoy seguro de que esto realmente diga que las cercas funcionan como cercas de hardware. –

Cuestiones relacionadas