2008-10-28 8 views
14

Editar: El código aquí todavía tiene algunos errores, y podría funcionar mejor en el departamento de rendimiento, pero en lugar de tratar de solucionarlo, para el registro llevé el problema a los grupos de discusión de Intel y tiene un montón de gran respuesta, y si todo va bien una versión pulida de flotación Atómica serán incluidos en una versión futura cerca de Threading Building Blocks de Intel¿Es segura la implementación de C++ para una flotación atómica?

autorización aquí es una pregunta difícil, quiero un flotador Atómica, no para rendimiento de gráficos superrápido, pero para usar rutinariamente como miembros de datos de las clases. Y no quiero pagar el precio de usar bloqueos en estas clases, porque no proporciona beneficios adicionales para mis necesidades.

Ahora con Intel tbb y otras bibliotecas atómicas que he visto, se admiten tipos enteros, pero no puntos flotantes. Así que seguí e implementé uno, y funciona ... pero no estoy seguro si REALMENTE funciona, o tengo mucha suerte de que funcione.

¿Alguien aquí sabe si esto no es una herejía?

typedef unsigned int uint_32; 

    struct AtomicFloat 
    { 
    private: 
    tbb::atomic<uint_32> atomic_value_; 

    public: 
    template<memory_semantics M> 
    float fetch_and_store(float value) 
    { 
     const uint_32 value_ = atomic_value_.tbb::atomic<uint_32>::fetch_and_store<M>((uint_32&)value); 
     return reinterpret_cast<const float&>(value_); 
    } 

    float fetch_and_store(float value) 
    { 
     const uint_32 value_ = atomic_value_.tbb::atomic<uint_32>::fetch_and_store((uint_32&)value); 
     return reinterpret_cast<const float&>(value_); 
    } 

    template<memory_semantics M> 
    float compare_and_swap(float value, float comparand) 
    { 
     const uint_32 value_ = atomic_value_.tbb::atomic<uint_32>::compare_and_swap<M>((uint_32&)value,(uint_32&)compare); 
     return reinterpret_cast<const float&>(value_); 
    } 

    float compare_and_swap(float value, float compare) 
    { 
     const uint_32 value_ = atomic_value_.tbb::atomic<uint_32>::compare_and_swap((uint_32&)value,(uint_32&)compare); 
     return reinterpret_cast<const float&>(value_); 
    } 

    operator float() const volatile // volatile qualifier here for backwards compatibility 
    { 
     const uint_32 value_ = atomic_value_; 
     return reinterpret_cast<const float&>(value_); 
    } 

    float operator=(float value) 
    { 
     const uint_32 value_ = atomic_value_.tbb::atomic<uint_32>::operator =((uint_32&)value); 
     return reinterpret_cast<const float&>(value_); 
    } 

    float operator+=(float value) 
    { 
     volatile float old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<float&>(atomic_value_); 
      new_value_ = old_value_ + value; 
     } while(compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); 
    } 

    float operator*=(float value) 
    { 
     volatile float old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<float&>(atomic_value_); 
      new_value_ = old_value_ * value; 
     } while(compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); 
    } 

    float operator/=(float value) 
    { 
     volatile float old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<float&>(atomic_value_); 
      new_value_ = old_value_/value; 
     } while(compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); 
    } 

    float operator-=(float value) 
    { 
     return this->operator+=(-value); 
    } 

    float operator++() 
    { 
     return this->operator+=(1); 
    } 

    float operator--() 
    { 
     return this->operator+=(-1); 
    } 

    float fetch_and_add(float addend) 
    { 
     return this->operator+=(-addend); 
    } 

    float fetch_and_increment() 
    { 
     return this->operator+=(1); 
    } 

    float fetch_and_decrement() 
    { 
     return this->operator+=(-1); 
    } 
    }; 

¡Gracias!

Editar: cambiada size_t a uint32_t como se sugiere Greg Rogers, de esa manera su más portátil

Editar: añade lista para toda la cosa, con algunas correcciones.

más ediciones: En cuanto al rendimiento utilizando un flotador bloqueado para 5.000.000 + = 100 operaciones con roscas en mi máquina se lleva a 3.6s, mientras mi flotador atómica, incluso con su tonta do-while tarda 0,2 segundos para hacer lo mismo trabajo. Entonces, el aumento del rendimiento> 30x significa que vale la pena (y esta es la captura) si es correcto.

Incluso más ediciones: Como Awgn señaló que mis fetch_and_xxxx piezas estaban todas mal. Solucionado y eliminado partes de la API de las que no estoy seguro (modelos de memoria con plantillas). E implementó otras operaciones en términos de operador + = para evitar la repetición del código

Agregado: Operador agregado * = y operador/=, ya que los flotantes no serían flotantes sin ellos. Gracias al comentario de Peterchen que esto se notó

Editar: La última versión del código de la siguiente manera (Voy a dejar la versión antigua de referencia aunque)

#include <tbb/atomic.h> 
    typedef unsigned int  uint_32; 
    typedef __TBB_LONG_LONG  uint_64; 

    template<typename FLOATING_POINT,typename MEMORY_BLOCK> 
    struct atomic_float_ 
    { 
    /* CRC Card ----------------------------------------------------- 
    | Class:   atmomic float template class 
    | 
    | Responsability: handle integral atomic memory as it were a float, 
    |     but partially bypassing FPU, SSE/MMX, so it is 
    |     slower than a true float, but faster and smaller 
    |     than a locked float. 
    |      *Warning* If your float usage is thwarted by 
    |     the A-B-A problem this class isn't for you 
    |      *Warning* Atomic specification says we return, 
    |     values not l-values. So (i = j) = k doesn't work. 
    | 
    | Collaborators: intel's tbb::atomic handles memory atomicity 
    ----------------------------------------------------------------*/ 
    typedef typename atomic_float_<FLOATING_POINT,MEMORY_BLOCK> self_t; 

    tbb::atomic<MEMORY_BLOCK> atomic_value_; 

    template<memory_semantics M> 
    FLOATING_POINT fetch_and_store(FLOATING_POINT value) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::fetch_and_store<M>((MEMORY_BLOCK&)value); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    FLOATING_POINT fetch_and_store(FLOATING_POINT value) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::fetch_and_store((MEMORY_BLOCK&)value); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    template<memory_semantics M> 
    FLOATING_POINT compare_and_swap(FLOATING_POINT value, FLOATING_POINT comparand) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::compare_and_swap<M>((MEMORY_BLOCK&)value,(MEMORY_BLOCK&)compare); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    FLOATING_POINT compare_and_swap(FLOATING_POINT value, FLOATING_POINT compare) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::compare_and_swap((MEMORY_BLOCK&)value,(MEMORY_BLOCK&)compare); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    operator FLOATING_POINT() const volatile // volatile qualifier here for backwards compatibility 
    { 
     const MEMORY_BLOCK value_ = atomic_value_; 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    //Note: atomic specification says we return the a copy of the base value not an l-value 
    FLOATING_POINT operator=(FLOATING_POINT rhs) 
    { 
     const MEMORY_BLOCK value_ = atomic_value_.tbb::atomic<MEMORY_BLOCK>::operator =((MEMORY_BLOCK&)rhs); 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    //Note: atomic specification says we return an l-value when operating among atomics 
    self_t& operator=(self_t& rhs) 
    { 
     const MEMORY_BLOCK value_ = atomic_value_.tbb::atomic<MEMORY_BLOCK>::operator =((MEMORY_BLOCK&)rhs); 
     return *this; 
    } 

    FLOATING_POINT& _internal_reference() const 
    { 
     return reinterpret_cast<FLOATING_POINT&>(atomic_value_.tbb::atomic<MEMORY_BLOCK>::_internal_reference()); 
    } 

    FLOATING_POINT operator+=(FLOATING_POINT value) 
    { 
     FLOATING_POINT old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<FLOATING_POINT&>(atomic_value_); 
      new_value_ = old_value_ + value; 
     //floating point binary representation is not an issue because 
     //we are using our self's compare and swap, thus comparing floats and floats 
     } while(self_t::compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); //return resulting value 
    } 

    FLOATING_POINT operator*=(FLOATING_POINT value) 
    { 
     FLOATING_POINT old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<FLOATING_POINT&>(atomic_value_); 
      new_value_ = old_value_ * value; 
     //floating point binary representation is not an issue becaus 
     //we are using our self's compare and swap, thus comparing floats and floats 
     } while(self_t::compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); //return resulting value 
    } 

    FLOATING_POINT operator/=(FLOATING_POINT value) 
    { 
     FLOATING_POINT old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<FLOATING_POINT&>(atomic_value_); 
      new_value_ = old_value_/value; 
     //floating point binary representation is not an issue because 
     //we are using our self's compare and swap, thus comparing floats and floats 
     } while(self_t::compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); //return resulting value 
    } 

    FLOATING_POINT operator-=(FLOATING_POINT value) 
    { 
     return this->operator+=(-value); //return resulting value 
    } 

    //Prefix operator 
    FLOATING_POINT operator++() 
    { 
     return this->operator+=(1); //return resulting value 
    } 

    //Prefix operator 
    FLOATING_POINT operator--() 
    { 
     return this->operator+=(-1); //return resulting value 
    } 

    //Postfix operator 
    FLOATING_POINT operator++(int) 
    { 
     const FLOATING_POINT temp = this; 
     this->operator+=(1); 
     return temp//return resulting value 
    } 

    //Postfix operator 
    FLOATING_POINT operator--(int) 
    { 
     const FLOATING_POINT temp = this; 
     this->operator+=(1); 
     return temp//return resulting value 
    } 

    FLOATING_POINT fetch_and_add(FLOATING_POINT addend) 
    { 
     const FLOATING_POINT old_value_ = atomic_value_; 
     this->operator+=(addend); 
     //atomic specification requires returning old value, not new one as in operator x= 
     return old_value_; 
    } 

    FLOATING_POINT fetch_and_increment() 
    { 
     const FLOATING_POINT old_value_ = atomic_value_; 
     this->operator+=(+1); 
     //atomic specification requires returning old value, not new one as in operator x= 
     return old_value_; 
    } 

    FLOATING_POINT fetch_and_decrement() 
    { 
     const FLOATING_POINT old_value_ = atomic_value_; 
     this->operator+=(-1); 
     //atomic specification requires returning old value, not new one as in operator x= 
     return old_value_; 
    } 
    }; 

    typedef atomic_float_<float,uint_32> AtomicFloat; 
    typedef atomic_float_<double,uint_64> AtomicDouble; 
+0

Es un poco mal por el operador = para devolver un valor, ya que en los tipos internos que evalúa a un valor-i (T & donde T es el tipo). Para esos tipos "(i = j) = k" es inusual pero legal, y asigna el valor de k a i. –

+0

Un buen punto, y abordado en mi última versión del código en las respuestas.Sin embargo, devolver T y no el valor l para operador = es el comportamiento correcto para valores atómicos en tbb. –

+0

hey @RobertGould. Muchas gracias por implementar la funcionalidad y compartirla aquí. Tengo dos preguntas: (1) ¿los tiempos siguen siendo válidos? Quiero decir, en mi plataforma, no puedo acelerar cuando uso la versión atómica en comparación con 'std :: mutex', (2) ¿hay alguna licencia para este código? ¿Qué debo hacer si me lo prestan y lo hago funcionar con 'std :: atomic' en la biblioteca estándar para mi proyecto? –

Respuesta

5

Recomendaría seriamente en contra de la herencia pública. No sé cómo es la implementación atómica, pero supongo que ha sobrecargado a los operadores que la usan como tipo integral, lo que significa que esas promociones se usarán en lugar de su flotador en muchos (¿la mayoría?) Casos.

no veo ninguna razón por la que no quiere trabajar, pero al igual que tengo a manera de probar que ...

Una nota: su rutina operator float() no tiene carga adquirir la semántica, y ¿No debería marcarse como const volátil (o definitivamente al menos const)?

EDIT: Si va a proporcionar el operador - (+) debe proporcionar ambas formas de prefijo/postfix.

+1

Hacer la composición es probablemente la mejor solución. Probablemente debería refactorizar la clase si la implementación está bien. –

+0

Totalmente de acuerdo con intheritance - composición. – xtofl

3

Parece que su aplicación supone que sizeof(size_t) == sizeof(float). ¿Eso siempre será cierto para tus plataformas de destino?

Y yo no diría enhebrar herejía tanto como fundición herejía. :)

+0

Bueno, no necesariamente, pero planeo poner una afirmación estática que compare sizeof (float) == sizeof (size_t) como protección para la compilación –

+0

¿De qué te sirve eso si usas uint32_t? –

+0

¡Buen punto amigo! –

0

De mi lectura de ese código, que sería muy enojado con un compilador como para apagar el montaje de este que no era atómica.

0

Haga que su compilador genere código de ensamblado y échele un vistazo. Si la operación es más que una instrucción en lenguaje ensamblador, entonces es no una operación atómica, y requiere bloqueos para operar correctamente en sistemas multiprocesador.

Desafortunadamente, no estoy seguro de que lo contrario también sea cierto: las operaciones de instrucción única son, garantizadas por ser atómicas. No conozco los detalles de la programación multiprocesador hasta ese nivel. Podría hacer un caso para cualquiera de los resultados. (Si alguien más tiene alguna información definitiva sobre eso, siéntete libre de entrar).

+0

Las instrucciones de ASM únicas se deben considerar no atómicas hasta que se demuestre lo contrario, especialmente en x86 y otras arquitecturas CISCy, ya que una instrucción se divide en microoperaciones, entre las cuales puede tener un cambio de contexto. Los insumos atómicos como las interrupciones de deshabilitar comparar e intercambiar para eludir esto. –

+0

Las instrucciones de lenguaje de ensamblaje único no son atómicas en sistemas con varios procesadores, independientemente de si alguno de los procesadores realiza un cambio de contexto. La forma de obtener la atomicidad es usar operaciones especialmente diseñadas para él, como compare-and-swap, o lock, o el algoritmo de Dekker. –

+0

Por supuesto, en un sistema multiprocesador, el cambio de contexto en sí mismo es irrelevante, pero el hecho de que debe examinar cada intercalación posible de la ejecución de subprocesos no cambia si múltiples subprocesos se multiplexan temporalmente en un núcleo o se multiplexan en tiempo memoria compartida. –

1

Este es el estado del código tal como está ahora después de las conversaciones en los tableros de inteligencia, pero todavía no se ha verificado completamente para funcionar correctamente en todos los escenarios.

#include <tbb/atomic.h> 
    typedef unsigned int  uint_32; 
    typedef __TBB_LONG_LONG  uint_64; 

    template<typename FLOATING_POINT,typename MEMORY_BLOCK> 
    struct atomic_float_ 
    { 
    /* CRC Card ----------------------------------------------------- 
    | Class:   atmomic float template class 
    | 
    | Responsability: handle integral atomic memory as it were a float, 
    |     but partially bypassing FPU, SSE/MMX, so it is 
    |     slower than a true float, but faster and smaller 
    |     than a locked float. 
    |      *Warning* If your float usage is thwarted by 
    |     the A-B-A problem this class isn't for you 
    |      *Warning* Atomic specification says we return, 
    |     values not l-values. So (i = j) = k doesn't work. 
    | 
    | Collaborators: intel's tbb::atomic handles memory atomicity 
    ----------------------------------------------------------------*/ 
    typedef typename atomic_float_<FLOATING_POINT,MEMORY_BLOCK> self_t; 

    tbb::atomic<MEMORY_BLOCK> atomic_value_; 

    template<memory_semantics M> 
    FLOATING_POINT fetch_and_store(FLOATING_POINT value) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::fetch_and_store<M>((MEMORY_BLOCK&)value); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    FLOATING_POINT fetch_and_store(FLOATING_POINT value) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::fetch_and_store((MEMORY_BLOCK&)value); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    template<memory_semantics M> 
    FLOATING_POINT compare_and_swap(FLOATING_POINT value, FLOATING_POINT comparand) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::compare_and_swap<M>((MEMORY_BLOCK&)value,(MEMORY_BLOCK&)compare); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    FLOATING_POINT compare_and_swap(FLOATING_POINT value, FLOATING_POINT compare) 
    { 
     const MEMORY_BLOCK value_ = 
      atomic_value_.tbb::atomic<MEMORY_BLOCK>::compare_and_swap((MEMORY_BLOCK&)value,(MEMORY_BLOCK&)compare); 
     //atomic specification requires returning old value, not new one 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    operator FLOATING_POINT() const volatile // volatile qualifier here for backwards compatibility 
    { 
     const MEMORY_BLOCK value_ = atomic_value_; 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    //Note: atomic specification says we return the a copy of the base value not an l-value 
    FLOATING_POINT operator=(FLOATING_POINT rhs) 
    { 
     const MEMORY_BLOCK value_ = atomic_value_.tbb::atomic<MEMORY_BLOCK>::operator =((MEMORY_BLOCK&)rhs); 
     return reinterpret_cast<const FLOATING_POINT&>(value_); 
    } 

    //Note: atomic specification says we return an l-value when operating among atomics 
    self_t& operator=(self_t& rhs) 
    { 
     const MEMORY_BLOCK value_ = atomic_value_.tbb::atomic<MEMORY_BLOCK>::operator =((MEMORY_BLOCK&)rhs); 
     return *this; 
    } 

    FLOATING_POINT& _internal_reference() const 
    { 
     return reinterpret_cast<FLOATING_POINT&>(atomic_value_.tbb::atomic<MEMORY_BLOCK>::_internal_reference()); 
    } 

    FLOATING_POINT operator+=(FLOATING_POINT value) 
    { 
     FLOATING_POINT old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<FLOATING_POINT&>(atomic_value_); 
      new_value_ = old_value_ + value; 
     //floating point binary representation is not an issue because 
     //we are using our self's compare and swap, thus comparing floats and floats 
     } while(self_t::compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); //return resulting value 
    } 

    FLOATING_POINT operator*=(FLOATING_POINT value) 
    { 
     FLOATING_POINT old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<FLOATING_POINT&>(atomic_value_); 
      new_value_ = old_value_ * value; 
     //floating point binary representation is not an issue becaus 
     //we are using our self's compare and swap, thus comparing floats and floats 
     } while(self_t::compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); //return resulting value 
    } 

    FLOATING_POINT operator/=(FLOATING_POINT value) 
    { 
     FLOATING_POINT old_value_, new_value_; 
     do 
     { 
      old_value_ = reinterpret_cast<FLOATING_POINT&>(atomic_value_); 
      new_value_ = old_value_/value; 
     //floating point binary representation is not an issue because 
     //we are using our self's compare and swap, thus comparing floats and floats 
     } while(self_t::compare_and_swap(new_value_,old_value_) != old_value_); 
     return (new_value_); //return resulting value 
    } 

    FLOATING_POINT operator-=(FLOATING_POINT value) 
    { 
     return this->operator+=(-value); //return resulting value 
    } 

    //Prefix operator 
    FLOATING_POINT operator++() 
    { 
     return this->operator+=(1); //return resulting value 
    } 

    //Prefix operator 
    FLOATING_POINT operator--() 
    { 
     return this->operator+=(-1); //return resulting value 
    } 

    //Postfix operator 
    FLOATING_POINT operator++(int) 
    { 
     const FLOATING_POINT temp = this; 
     this->operator+=(1); 
     return temp//return resulting value 
    } 

    //Postfix operator 
    FLOATING_POINT operator--(int) 
    { 
     const FLOATING_POINT temp = this; 
     this->operator+=(1); 
     return temp//return resulting value 
    } 

    FLOATING_POINT fetch_and_add(FLOATING_POINT addend) 
    { 
     const FLOATING_POINT old_value_ = atomic_value_; 
     this->operator+=(addend); 
     //atomic specification requires returning old value, not new one as in operator x= 
     return old_value_; 
    } 

    FLOATING_POINT fetch_and_increment() 
    { 
     const FLOATING_POINT old_value_ = atomic_value_; 
     this->operator+=(+1); 
     //atomic specification requires returning old value, not new one as in operator x= 
     return old_value_; 
    } 

    FLOATING_POINT fetch_and_decrement() 
    { 
     const FLOATING_POINT old_value_ = atomic_value_; 
     this->operator+=(-1); 
     //atomic specification requires returning old value, not new one as in operator x= 
     return old_value_; 
    } 
    }; 

    typedef atomic_float_<float,uint_32> AtomicFloat; 
    typedef atomic_float_<double,uint_64> AtomicDouble; 
1

Aunque el tamaño de un uint32_tpuede ser equivalente a la de un flotadoren un arco dado, mediante una reinterpretación de un elenco de primera en la que está asumiendo implícitamente que los incrementos atómicos, decrementos y todo las otras operaciones en bits son semánticamente equivalentes en ambos tipos, que no son en realidad. Dudo que funcione como se esperaba.

+0

No, no, no, por eso estoy sacando las operaciones reales en una transacción mientras bucle (un patrón paralelo conocido). De todos modos, puedo asegurarle que el código funciona correctamente en un solo hilo. E incluso ha estado funcionando correctamente en multithread. Simplemente no estoy seguro si eso es algo en lo que puedo confiar .. –

+0

No presté mucha atención a los operadores. Pero la pregunta es: ¿está seguro de que fetch_and_add, fetch_and_increment, etc. están trabajando de la manera correcta? –

+0

¡Tienes razón! Realmente no les había pensado mucho desde que estaba probando los operadores. ¡Fetch_xxxx está todo mal! tonto, lo perdí, necesitan el mismo trato que los operadores. –

1

dudo mucho que usted obtenga los valores correctos en fetch_and_add etc, como la adición de flotación es diferente de la adición int.

Esto es lo que recibo de estas aritmética:

1 + 1 = 1.70141e+038 
100 + 1 = -1.46937e-037 
100 + 0.01 = 1.56743e+038 
23 + 42 = -1.31655e-036 

Así que sí, multi-hilo, pero no lo que esperas.

los algoritmos sin bloqueo (operador +, etc.) debe trabajar en relación con la atomicidad (no se han comprobado para el propio algoritmo ..)


otra solución: Como se trata de todas las adiciones y sustracciones, Es posible que pueda darle a cada tema su propia instancia y luego agregar los resultados de varios hilos.

+0

Nota que no estoy haciendo eso. Estoy convirtiendo los enteros en flotantes refs, lo que significa que se manejan correctamente. old_value_ = reinterpret_cast (* this); new_value_ = old_value_ + value; –

+0

Eso estaría bien para una solución para un "reducir", pero necesito flotadores como miembros de estructuras de datos (propiedades) que tienen vidas largas. Pero tu comentario me recuerda que las carrozas son tontas sin multiplicación y división. Voy a agregar esos también –

+0

¡El código revisado se ve mucho mejor! :) Y sí, los bucles sin cierre se ven bien para mí, pero no he hecho lo suficiente con aquellos para juzgar realmente. – peterchen

1

Solo una nota sobre esto (quería hacer un comentario pero aparentemente los nuevos usuarios no pueden comentar): Usar reinterpret_cast en las referencias produce un código incorrecto con gcc 4.1 -O3. Esto parece estar arreglado en 4.4 porque funciona. Cambiar los reinterpret_casts por punteros, aunque es un poco más feo, funciona en ambos casos.

Cuestiones relacionadas