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;
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. –
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. –
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? –