这是C ++实现一个原子浮动安全吗?

编辑:这里的代码还是有它的一些错误,并且它可以做在表演系的,而是试图解决这一问题更好,备案我把这个问题交给英特尔讨论组,得到了很多很好的意见,如果一切顺利的原子浮动抛光版本将包含在英特尔线程构建模块的近期发行

确定这里是一个艰难的,我希望有一个原子浮动,而不是超快的图形性能,但通常使用的类的数据成员。 我不希望支付使用锁对这些类的价格,因为它提供了我需要的不是更多的好处。

现在,英特尔TBB和我见过的其他原子库,整数类型的支持,而不是浮动点。 于是我就上实现的一个,和它的作品...但我不知道它是否真的有效,还是我只是很幸运,它的工作原理。

任何人都知道这里,如果这不是某种形式的线程异端?

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,而我的原子浮动即使其愚蠢的做,而需要0.2秒来完成同样的工作。 因此,> 30倍的性能提升意味着其价值的,(这是捕获),如果它的正确。

更编辑:正如AWGN指出我的fetch_and_xxxx部分都错了。 修正和删除的API的一部分,我不知道(模板内存模型)。 并实施其他业务的运营商而言+ =,以避免代码重复

补充:补充符* =和operator / =,因为花车不会彩车没有他们。 由于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 ABA 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;

--------------解决方案-------------

我会认真地建议对公有继承。 我不知道是什么的原子实现是一样的,但即时通讯假设它已经超负荷使用它作为整体式的,这意味着这些促销活动将被用来代替你浮在许多运营商(也许大多数?)的情况下。

我看不出有任何理由为什么这样做不行,但像你,我要的方式证明...

一注:您的operator float()程序不必负载获取的语义,应该不是被标记const的波动(或肯定至少常量)?

编辑:如果你要提供运营商 - (),你应该同时提供前缀/后缀形式。

它看起来像你实现假定sizeof(size_t) == sizeof(float) 将始终是真实的你的目标平台?

我不会说线程的异端这么多的铸造异端。 :)

虽然一个uint32_t的大小可以是等同于在给定的拱一个浮子 ,通过重新解释从一个铸造成其他您隐含假设原子递增,递减和所有位等操作在语义上等同于这两种类型的,这是不现实。 我怀疑它按预期工作。

我强烈怀疑你在fetch_and_add等正确的价值观,浮法另外是从int除了不同。

这是我从这些算术获得:

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

所以是的,线程安全的,但不是你所期望的。

无锁算法(运营商+等)应该工作有关原子性(有没有检查算法本身..)


其他的解决方案:由于它是所有加法和减法,你也许可以让每个线程它自己的实例,然后从多个线程增加的结果。

这是代码的状态,目前的情况是在英特尔主板会谈后,但仍没有得到彻底的验证,在所有情况下正常工作。

#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 ABA 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;

关于这只是说明(我想作评论,但显然是新用户不允许评论):上使用引用产生reinterpret_cast的不正确的代码使用GCC 4.1 -O3。 这似乎是因为它的工作原理是固定在4.4。 更改reinterpret_casts的指针,而稍微难看,工作在这两种情况下。

从我读的代码,我会很生气,这样的编译器,以推出集本,这不是原子。

让你的编译器生成汇编代码,并看看它。 如果操作是多单汇编语言指令,则不是一个原子操作,并且需要锁在多处理器系统正常运行。

不幸的是,我不能肯定,反过来也是如此-这单指令操作保证是原子。 我不知道多处理器编程的细节降至这一水平。 我可以做的任何结果的情况下。 (如果任何人有一些明确的信息,随意的附和。)

分类:C# 时间:2012-01-01 人气:0
分享到:

相关文章

  • 如何用在MFC中线程程序asyncronous电话 2012-11-30

    如何用在MFC中线程程序asyncronous电话 --------------解决方案------------- 你好Lathai, 由于该问题是与Microsoft基础类(MFC)编程,这个问题会更适合在MSDN支持论坛. 我建议你​​张贴以下链接的问题. http://social.msdn.microsoft.com/Forums/en-US/newthread?category=visualc&forum=vcmfcatl

  • 如何找到在列表中的一个原子的位置 2013-05-17

    我想在列表中找到一个原子的位置. (position-in-list 'a (abcde))给出了0 (position-in-list 'b (abcde) )给出了1 (position-in-list 'Z(abcde) )给出了零. 我已经贴我的代码只返回1. (defun position-in-list (letter list) ) ( cond ( (null list) nil ) ( (eq (car list) letter) count ) ( t (position-i

  • 你能信号和C#线程同步原子等待? 2013-07-23

    我在C#中的线程同步的一些问题. 我有得到由两个线程操作的共享对象,我已经使用锁()做访问相互排斥的对象,但我也希望这取决于共享对象的状态,以阻止的每个线程. 特别阻塞线程A当对象是空的,块线程B当对象是满的,并有其他线程信号阻塞的线程对象的状态发生变化时. 我试着用一个ManualResetEvent的这样做,但遇到了一个竞争条件,其中线程B将检测的对象是满了,搬到了WaitOne,线程A会进来,清空对象(信令MRE每次访问,并一度阻止本身该对象为空),线程A达到其之前的WaitOne,这意味

  • 线程安全的TBB懒创造? 2013-08-22

    在我的C ++代码,我保持一个指针应该被延后创建一个对象,也就是说,仅应要求创建. 我有下面的代码,这显然不是线程安全的. LAZY* get_lazy() { if (0 == _lazy) _lazy = create_lazy(); return _lazy; } 我不知道什么样的同步应该我在这里使用? 我知道Boost.thread提供支持一次性初始化. 但我希望,有利用TBB + C ++只是一个简单的解决方案. 我还应注意... 我无法创建_lazy作为一个静态对象(其实我是想保持这

  • 在Tomcat中线程睡眠 2013-02-03

    我写它接受传入SIP语音呼叫,然后投票上在同一台主机传入的声音请求在Tomcat实例中运行的Java应用程序Web服务的VXML应用程序(例如,播放音频提示或收集一些位数)一个单独的信道. 这些语音请求通过一个单独的WS接口接收和缓存的VXML会话收集. 语音请求可以从任何地方0.5秒接收呼叫被接受后,超过30秒. 从逻辑上讲,VXML会议应定期轮询新的要求和Tomcat中的Java应用程序返回指示是否已收到任何请求的无阻塞响应. 然而,一个附加约束我是从VXML解释使得Web服务调用的CPU成

  • 新在C#中线程,可你让线程方法和通用的危害有哪些? 2013-03-31

    我刚刚开始进入线程的想法,并想知道如果我能做出这样更抽象. 这两个foo和酒吧源于一个基类的方法,所以我想通过在一个或另一个,并能够使用推导的方法做的工作. 我也想知道如何正确地命名线程和线程里面的方法. if (ChkFoo.Checked) { Thread fooThread = new Thread(new ThreadStart(this.ThreadedFooMethod)); fooThread.Start(); } if (ChkBar.Checked) { Thread bar

  • C#中线程模式 - 这是个好主意吗? 2013-06-21

    我在玩我的今天的项目,并发现了一个有趣的小片段,给下面的模式,你可以安全地清理线程,即使它被迫关闭早期. 我的项目是它派生为每一个客户一个新的线程网络服务器. 我发现这个有用的提前终止从远程端,还可以从本地端(我可以叫.Abort()从内我处理的代码). 有什么问题可以在此看到,或者有什么建议,你会做给任何人看一个类似的方法? 测试情况如下: using System; using System.Threading; class Program { static Thread t1 = new

  • 差异事件处理Silverlight和WPF中 - 线程关联问题 2013-09-24

    我一直在开发在Silverlight的Lync Silverlight应用程序,现在我想将它转移到WPF. 不过,我面临着一些线程关联的问题. 比如我在一个文本块我的网页上显示的Lync客户端的状态,所以在我后面的代码了有线的状态改变的事件处理程序,写入新的国家进入正文块每当Lync客户端的状态发生改变. 现在,这完美地工作在Silverlight但似乎没有在WPF不允许的. 现在,我的问题是: 为什么它在Silverlight工程BT不WPF,即使Silverlight的应该是WPF的一个子集

  • C ++共享库中线程记录 2013-11-11

    我已经用C ++实现多线程共享库(对于Linux和Windows). 我想补充日志记录机制库本身的内部. 该库的调用者并不知道这一点. 日志文件会一样的,所以我想知道我怎么能设计出线程安全的日志记录,如果多个进程正在使用我的图书馆,并试图打开并登录到同一个日志文件. 有什么建议么? --------------解决方案------------- 使用文件锁定. 我相信,的fcntl是POSIX兼容的所以要在Windows上工作了. 请问您的代码中使用Posix的电话? 随着的fcntl,你应该能

  • 跨线程操作无效:从访问控制“listBox1中'>线程比它创建于其他线程 2013-11-11

    可能重复: 跨线程操作无效:控制从比它创建的线程以外的线程访问. 当我试图将项目添加到列表框我得到的C#下面的错误. 跨线程操作无效:控制'listBox1中"从比它创建的线程以外的线程访问. 代码 : private void Form1_Load(object sender, EventArgs e) { Jid jd = new Jid("USERNAME"); xmpp.Open(jd.User, "PASSWORD"); xmpp.OnLogin

Copyright (C) 55228885.com, All Rights Reserved.

55228885 版权所有 京ICP备15002868号

processed in 1.771 (s). 10 q(s)