2008-10-28 20 views
14

Chỉnh sửa: Mã ở đây vẫn còn một số lỗi trong đó, và nó có thể làm tốt hơn trong bộ phận hiệu suất, nhưng thay vì cố gắng khắc phục điều này, cho bản ghi tôi đã đưa vấn đề cho các nhóm thảo luận của Intel và nhận được rất nhiều phản hồi tuyệt vời, và nếu mọi thứ suôn sẻ, phiên bản đánh bóng của phao Atomic sẽ được đưa vào bản phát hành tương lai gần của các khối xây dựng luồng của IntelĐây có phải là cài đặt C++ cho phao Atomic an toàn không?

Ok đây là một vấn đề khó khăn. hiệu năng đồ họa siêu nhanh, nhưng sử dụng thường xuyên như các thành viên dữ liệu của các lớp. Và tôi không muốn trả giá bằng cách sử dụng khóa trên các lớp này, bởi vì nó không cung cấp thêm lợi ích cho nhu cầu của tôi.

Bây giờ với tbb của intel và các thư viện nguyên tử khác mà tôi đã thấy, các kiểu số nguyên được hỗ trợ, nhưng không phải là các dấu phẩy động. Vì vậy, tôi tiếp tục và thực hiện một, và nó hoạt động ... nhưng tôi không chắc chắn nếu nó thực sự làm việc, hoặc tôi chỉ rất may mắn rằng nó hoạt động.

Bất kỳ ai ở đây đều biết nếu đây không phải là một dạng dị giáo nào đó?

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); 
    } 
    }; 

Cảm ơn!

Edit: thay đổi size_t để uint32_t như Greg Rogers đề nghị, như vậy khả năng di chuyển

Chỉnh sửa của nó: thêm danh sách cho toàn bộ điều, với một số bản sửa lỗi.

Chỉnh sửa khác: Hiệu suất khôn ngoan khi sử dụng khóa bị khóa cho 5.000.000 + = hoạt động với 100 luồng trên máy của tôi mất 3,6 giây, trong khi phao nguyên tử của tôi thậm chí với công việc. Vì vậy, tăng hiệu suất> 30x có nghĩa là giá trị của nó, (và đây là bắt) nếu nó chính xác.

Thậm chí nhiều chỉnh sửa: Như Awgn đã chỉ ra các phần của tôi fetch_and_xxxx đều sai. Sửa lỗi đó và gỡ bỏ các phần của API mà tôi không chắc chắn (các mô hình bộ nhớ templated). Và triển khai các hoạt động khác về toán tử + = để tránh lặp lại mã

Đã thêm: Toán tử đã thêm * = và toán tử/=, vì phao sẽ không nổi nếu không có chúng. Nhờ bình luận Peterchen rằng điều này được chú ý

Edit: Phiên bản mới nhất của mã sau (Tôi sẽ rời khỏi phiên bản cũ để tham khảo mặc dù)

#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

Nó hơi ác đối với toán tử = để trả về một giá trị, vì trên các kiểu nội trang nó đánh giá thành một giá trị (T & T là loại). Đối với những loại "(i = j) = k" là bất thường nhưng hợp pháp, và gán giá trị của k cho i. –

+0

Một điểm tốt và được đề cập trong phiên bản mới nhất của tôi về mã trong câu trả lời.Tuy nhiên trở về T và không phải là lvalue cho toán tử = là hành vi chính xác cho các giá trị nguyên tử trong tbb. –

+0

hey @RobertGould. Cảm ơn bạn rất nhiều vì đã triển khai chức năng này và chia sẻ nó ở đây. Tôi có hai câu hỏi: (1) là thời gian vẫn còn hợp lệ? Tôi có nghĩa là, trên nền tảng của tôi, tôi không thể có được tốc độ khi tôi sử dụng phiên bản nguyên tử như trái ngược với 'std :: mutex', (2) là có một số giấy phép cho đoạn mã này? Tôi nên làm gì nếu tôi mượn nó và làm cho nó hoạt động với 'std :: atomic' trong thư viện chuẩn cho dự án của tôi? –

Trả lời

5

Tôi rất muốn tư vấn chống lại sự thừa kế công khai. Tôi không biết việc thực thi nguyên tử là như thế nào, nhưng im giả sử nó có các toán tử quá tải sử dụng nó như kiểu tách rời, có nghĩa là các chương trình khuyến mãi đó sẽ được sử dụng thay cho float của bạn trong nhiều trường hợp (có thể là nhiều nhất).

Tôi không thấy bất kỳ lý do tại sao điều đó sẽ không làm việc, nhưng cũng giống như bạn tôi phải cách để chứng minh điều đó ...

Một lưu ý: thói quen operator float() của bạn không có tải được ngữ nghĩa, và không nên nó được đánh dấu const dễ bay hơi (hoặc chắc chắn ít nhất là const)?

EDIT: Nếu bạn định cung cấp toán tử -(), bạn nên cung cấp cả hai dạng tiền tố/postfix.

+1

Làm bố cục có lẽ là giải pháp tốt hơn. Tôi có lẽ nên refactor lớp nếu việc thực hiện là ok. –

+0

Hoàn toàn đồng ý với nội dung - thành phần. – xtofl

3

Dường như thực hiện của bạn giả định rằng sizeof(size_t) == sizeof(float). Điều đó luôn đúng với các nền tảng mục tiêu của bạn?

Và tôi sẽ không nói luồng dị giáo quá nhiều như truyền dị giáo. :)

+0

Cũng không nhất thiết, nhưng tôi có kế hoạch đặt một khẳng định tĩnh so sánh sizeof (float) == sizeof (size_t) với tư cách là người bảo vệ để biên dịch –

+0

Điều gì khiến bạn vượt qua chỉ bằng uint32_t? –

+0

Điểm tốt bạn của tôi! –

0

Từ đọc của tôi về mã đó, tôi sẽ thực sự phát điên với trình biên dịch như vậy để đưa ra lắp ráp cho điều này không phải là nguyên tử.

0

Trình biên dịch của bạn tạo mã lắp ráp và xem xét nó. Nếu thao tác không chỉ là một lệnh ngôn ngữ lắp ráp đơn, thì đó là không phải là hoạt động nguyên tử và yêu cầu ổ khóa hoạt động đúng trong các hệ thống đa xử lý.

Thật không may, tôi không chắc chắn điều ngược lại cũng đúng - các thao tác đơn hướng dẫn được đảm bảo là nguyên tử. Tôi không biết chi tiết của chương trình đa xử lý xuống cấp đó. Tôi có thể tạo ra một trường hợp cho cả hai kết quả. (Nếu bất kỳ ai khác có thông tin dứt khoát về điều đó, hãy thoải mái tham gia.)

+0

Hướng dẫn ASM đơn nên được coi là không nguyên tử cho đến khi được chứng minh bằng cách khác, đặc biệt là trên kiến ​​trúc x86 và CISCy khác, vì hướng dẫn được chia nhỏ thành micro-op, betwixt mà bạn có thể có một chuyển đổi ngữ cảnh. Các nguyên tử nguyên tử như các ngắt vô hiệu hóa so sánh và hoán đổi để giải thích điều này. –

+0

Hướng dẫn ngôn ngữ lắp ráp đơn không phải là nguyên tử trong các hệ thống đa xử lý bất kể bộ xử lý có chuyển đổi ngữ cảnh hay không. Cách để có được nguyên tử là sử dụng các hoạt động được thiết kế đặc biệt cho nó, chẳng hạn như so sánh và trao đổi, hoặc khóa, hoặc thuật toán của Dekker. –

+0

Tất nhiên, trong một hệ thống đa xử lý, bối cảnh chuyển đổi chính nó là không liên quan, nhưng thực tế là bạn nên kiểm tra mọi xen kẽ thực thi luồng có thể không thay đổi cho dù nhiều luồng được tùy ý ghép kênh vào một lõi hay ghép kênh theo thời gian bộ nhớ dùng chung. –

1

Đây là trạng thái của mã ngay bây giờ sau khi nói trên bảng mạch, nhưng vẫn chưa được xác minh kỹ lưỡng để hoạt động chính xác trong mọi tình huống.

#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

Mặc dù kích thước của một uint32_t có thể tương đương với một phao trên vòm nhất định, bởi reinterpreting một dàn diễn viên từ một thành khác mà bạn đang ngầm giả định rằng gia số nguyên tử, suất này và tất cả các hoạt động khác trên bit tương đương về mặt ngữ nghĩa trên cả hai loại, không có trong thực tế. Tôi nghi ngờ nó hoạt động như mong đợi.

+0

Không, không, tôi không, đó là lý do tại sao tôi kéo các hoạt động thực tế ra thành một giao dịch trong khi vòng lặp (một mô hình song song đã biết). Dù sao tôi có thể đảm bảo với bạn mã hoạt động chính xác trong một chủ đề duy nhất. Và nó thậm chí đã hoạt động chính xác trong đa luồng. Chỉ cần không chắc chắn nếu đó là một cái gì đó tôi có thể tin tưởng .. –

+0

Tôi đã không chú ý nhiều đến các nhà khai thác. Nhưng câu hỏi đặt ra là: bạn có chắc rằng fetch_and_add, fetch_and_increment v.v. đang hoạt động đúng cách không? –

+0

Bạn đã đúng! Tôi đã không thực sự đưa ra nhiều suy nghĩ cho họ kể từ khi tôi đã thử nghiệm các nhà khai thác. Hàm fetch_xxxx đều sai! ngớ ngẩn tôi đã bỏ lỡ điều đó, họ cần sự đối xử tương tự với các nhà khai thác. –

1

Tôi thực sự nghi ngờ rằng bạn nhận được các giá trị chính xác trong fetch_and_add v.v., khi bổ sung float khác với bổ sung.

Đây là những gì tôi nhận được từ những arithmetics:

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

Vì vậy, yeah, threadsafe nhưng không phải những gì bạn mong đợi.

các thuật toán lock-free (operator + vv) phải làm việc liên quan đến số nguyên tử (chưa kiểm tra các thuật toán riêng của mình ..)


giải pháp khác: Vì nó là tất cả các bổ sung và subtractions, bạn có thể cung cấp cho mỗi chủ đề cá thể riêng của nó, sau đó thêm các kết quả từ nhiều luồng.

+0

Lưu ý tôi không làm điều đó. Tôi đang đúc các int vào float refs, có nghĩa là chúng được xử lý một cách chính xác. old_value_ = reinterpret_cast (* điều này); new_value_ = old_value_ + value; –

+0

Điều đó sẽ là tốt cho một giải pháp cho một "giảm", nhưng tôi cần nổi như là thành viên của cấu trúc dữ liệu (tài sản) có cuộc sống lâu dài. Nhưng bình luận của bạn không nhắc tôi nổi là ngớ ngẩn mà không có phép nhân và chia. Ngoài ra, hãy thêm những mã đó quá –

+0

Mã đã sửa đổi trông đẹp hơn nhiều! :) Và có, các vòng không có khóa trông ổn với tôi, nhưng tôi đã không làm đủ với những người thực sự đánh giá. – peterchen

1

Chỉ cần lưu ý về điều này (Tôi muốn đưa ra nhận xét nhưng dường như người dùng mới không được phép nhận xét): Sử dụng reinterpret_cast trên tham chiếu tạo mã không chính xác với gcc 4.1 -O3. Điều này dường như được sửa trong 4.4 vì nó hoạt động. Thay đổi reinterpret_casts thành con trỏ, trong khi hơi xấu xí hơn, hoạt động trong cả hai trường hợp.