संपादित करें: यहां कोड में अभी भी कुछ कीड़े हैं, और यह प्रदर्शन विभाग में बेहतर प्रदर्शन कर सकती है, लेकिन इसे ठीक करने की कोशिश करने के बजाय, रिकॉर्ड के लिए मैंने समस्या को इंटेल चर्चा समूहों में लिया और महान प्रतिक्रिया के बहुत सारे मिल गया है, और अगर सब कुछ ठीक परमाणु नाव के एक पॉलिश संस्करणक्या यह परमाणु फ्लोट सुरक्षित के लिए यह C++ कार्यान्वयन सुरक्षित है?
ठीक है यहाँ एक कठिन एक, मैं एक परमाणु नाव चाहते है, के लिए नहीं इंटेल की थ्रेडिंग बिल्डिंग ब्लॉक्स के पास भविष्य के रिलीज में शामिल किया जाएगा सुपर-फास्ट ग्राफिक्स प्रदर्शन, लेकिन नियमित रूप से कक्षाओं के डेटा-सदस्यों के रूप में उपयोग करने के लिए। और मैं इन वर्गों पर ताले का उपयोग करने की कीमत का भुगतान नहीं करना चाहता, क्योंकि यह मेरी आवश्यकताओं के लिए कोई अतिरिक्त लाभ नहीं प्रदान करता है।
अब इंटेल के टीबीबी और अन्य परमाणु पुस्तकालयों के साथ मैंने देखा है, पूर्णांक प्रकार समर्थित हैं, लेकिन फ्लोटिंग पॉइंट नहीं हैं। तो मैंने आगे बढ़कर एक कार्यान्वित किया, और यह काम करता है ... लेकिन मुझे यकीन नहीं है कि यह वास्तव में काम करता है, या मैं बहुत भाग्यशाली हूं कि यह काम करता है।
कोई भी यहां जानता है कि यह थ्रेडिंग थ्रेशिंग का कोई रूप नहीं है?
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);
}
};
धन्यवाद!
संपादित करें: बदली हुई size_t uint32_t के रूप में ग्रेग रोजर्स का सुझाव दिया, कि जिस तरह से अपने अधिक पोर्टेबल
संपादित करें: पूरे बात के लिए लिस्टिंग कहा, कुछ सुधारों के साथ।
अधिक संपादन: प्रदर्शन बुद्धिमान एक बंद नाव का उपयोग कर 5.000.000 + = मेरी मशीन पर 100 धागे के साथ संचालन के लिए 3.6s लेता है, जबकि यहां तक कि अपने मूर्खतापूर्ण do-, जबकि साथ अपने परमाणु नाव 0.2s लेता भी ऐसा ही करने काम। तो> 30x प्रदर्शन बढ़ावा का अर्थ यह इसके लायक है, (और यह पकड़ है) यदि यह सही है।
और भी संपादन: जैसा कि अज्ञान ने मेरी fetch_and_xxxx
भागों को इंगित किया था, सभी गलत थे। एपीआई के हिस्सों को फिक्स्ड और हटा दिया गया है, मुझे यकीन नहीं है (टेम्पलेट मेमोरी मॉडल)। और कोड पुनरावृत्ति से बचने के लिए ऑपरेटर + = के संदर्भ में अन्य परिचालनों को कार्यान्वित किया
जोड़ा गया: ऑपरेटर * = और ऑपरेटर/= जोड़ा गया, क्योंकि फ्लोट उनके बिना तैरता नहीं होगा। Peterchen की टिप्पणी के लिए धन्यवाद है कि इस देखा गया था
संपादित करें: कोड का नवीनतम संस्करण इस प्रकार है (मैं पुराने संस्करण में संदर्भ के लिए हालांकि छोड़ देंगे)
#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;
ऑपरेटर के लिए यह थोड़ा बुरा है = एक मूल्य वापस करने के लिए, क्योंकि बिल्टिन प्रकारों पर यह एक लाभा (टी और जहां टी प्रकार है) का मूल्यांकन करता है। उन प्रकारों के लिए "(i = j) = k" असामान्य लेकिन कानूनी है, और i के मान को असाइन करता है I –
एक अच्छा बिंदु, और उत्तर में कोड के मेरे नवीनतम संस्करण में संबोधित किया।हालांकि टी को वापस करना और ऑपरेटर के लिए lvalue नहीं है = टीबीबी में परमाणु मूल्यों के लिए सही व्यवहार है। –
हे @RobertGould। कार्यक्षमता को लागू करने और इसे साझा करने के लिए बहुत बहुत धन्यवाद। मेरे पास दो प्रश्न हैं: (1) क्या समय अभी भी वैध हैं? मेरा मतलब है, मेरे प्लेटफॉर्म पर, जब मैं 'std :: mutex' के विपरीत परमाणु संस्करण का उपयोग करता हूं, तो मुझे स्पीड-अप नहीं मिल सकता है, (2) क्या कोड के इस टुकड़े के लिए कुछ लाइसेंस है? अगर मैं इसे उधार लेना चाहता हूं और इसे अपनी परियोजना के लिए मानक पुस्तकालय में 'std :: atomic' के साथ काम करना चाहता हूं तो मुझे क्या करना चाहिए? –