Sca*_*ark 7 c++ templates atomic c++11
我编写了followin原子模板,以模仿即将推出的c ++ 0x标准中可用的原子操作.
但是,我不确定在返回基础值时我有__sync_synchronize()调用是必要的.
根据我的理解,__sync_synchronize()是一个完整的内存屏障,我不确定在返回对象值时我需要这么昂贵的调用.
我很确定在值的设置周围需要它,但我也可以用程序集来实现它.
__asm__ __volatile__ ( "rep;nop": : :"memory" );
Run Code Online (Sandbox Code Playgroud)
有谁知道我确实需要在返回对象时使用synchronize().
M.
template < typename T >
struct atomic
{
private:
volatile T obj;
public:
atomic( const T & t ) :
obj( t )
{
}
inline operator T()
{
__sync_synchronize(); // Not sure this is overkill
return obj;
}
inline atomic< T > & operator=( T val )
{
__sync_synchronize(); // Not sure if this is overkill
obj = val;
return *this;
}
inline T operator++()
{
return __sync_add_and_fetch( &obj, (T)1 );
}
inline T operator++( int )
{
return __sync_fetch_and_add( &obj, (T)1 );
}
inline T operator+=( T val )
{
return __sync_add_and_fetch( &obj, val );
}
inline T operator--()
{
return __sync_sub_and_fetch( &obj, (T)1 );
}
inline T operator--( int )
{
return __sync_fetch_and_sub( &obj, (T)1 );
}
inline T operator-=( T )
{
return __sync_sub_and_fetch( &obj, val );
}
// Perform an atomic CAS operation
// returning the value before the operation
inline T exchange( T oldVal, T newVal )
{
return __sync_val_compare_and_swap( &obj, oldval, newval );
}
};
Run Code Online (Sandbox Code Playgroud)
更新:由于编译器优化,我希望确保操作在读/写重新排序方面是一致的.
volatile T obj;
Run Code Online (Sandbox Code Playgroud)
volatile在这里毫无用处,更是你自己制造了所有障碍.
inline T operator++( int )
Run Code Online (Sandbox Code Playgroud)
内联是不必要的,因为在类中定义方法时暗示了它.
inline operator T()
{
__sync_synchronize(); // (I)
T tmp=obj;
__sync_synchronize(); // (II)
return tmp;
}
inline atomic< T > & operator=( T val )
{
__sync_synchronize(); // (III)
obj = val;
__sync_synchronize(); // (IV)
return *this;
}
Run Code Online (Sandbox Code Playgroud)
为了确保读写时内存访问的总排序,每次访问都需要两个障碍(如下所示).我很满意只有障碍(II)和(III),因为它们足以满足我提出的一些用途(例如指针/布尔说数据就在那里,自旋锁),但是,除非另有说明,否则我不会省略其他,因为有人可能需要它们(如果有人表示你可以在不限制可能的用途的情况下省略一些障碍,那将会很好,但我不认为这是可能的).
当然,这将是不必要的复杂和缓慢.
也就是说,我会抛弃障碍,甚至在类似模板的任何地方使用障碍的想法.注意:
顺便说一句,c ++ 0x接口允许您指定精确的内存排序约束.
nev*_*ord -1
inline operator T()
{
__sync_synchronize(); // Not sure this is overkill
return obj;
}
Run Code Online (Sandbox Code Playgroud)
简短版本:这太过分了。
长版:
为什么你想将此类实现为模板?这是没有意义的,因为原子操作只允许在 1-8 字节的整数类型上进行,您甚至无法确定所有平台都支持 8 字节整数。
您应该将原子类实现为非模板版本,并使用硬件/系统的“本机”整数类型。32 位处理器/操作系统上为 int32_t,64 位系统上为 int64_t。例如:
#ifdef ...
typedef ... native_int_type;
#endif
// verify that you choosed the correct integer type
BOOST_STATIC_ASSERT(sizeof(native_int_type) == sizeof(void*));
Run Code Online (Sandbox Code Playgroud)
BOOST_STATIC_ASSERT 直接从 C++0x 转向“static_assert()”。
如果您使用“完美适合”整数类型,您可以像这样编写运算符:
operator native_int_type() { return obj; }
Run Code Online (Sandbox Code Playgroud)
因为 obj 是易失性的,所以保证获取该值并且不返回任何缓存的值。而且因为您使用的是“本机”整数类型,所以您可以确定读取这样的值是原子的。
atomic& operator=( native_integer_type val )
Run Code Online (Sandbox Code Playgroud)
同样,如果您使用正确的整数类型,则不需要同步。在 intel 32 位系统上读取/设置 int32 是原子的,在 64 位系统上读取/设置 int64 也是原子的。
我没有看到将原子作为模板实现有任何好处。原子操作依赖于平台。最好提供一个“atomic_int”类,它只保证至少有 4 个字节(如果您支持 32 位和 64 位系统)和一个“atomic_pointer”(如果您需要)。这样,类的名称也暗示了语义和目的。
如果您只使用“原子”,人们可能会想:“哇,我只需要把我的字符串类放入这个模板中,然后它就是线程安全的!”。
编辑:回答您的更新:“我想确保由于编译器优化而导致读/写重新排序时操作是一致的。”
为了防止编译器和 CPU 重新排序读/写操作,您需要 __sync_synchronize()。
但请注意,获取/释放语义可能会比完全屏障产生更好的性能。
编辑2:
inline atomic< T > & operator=( T val )
{
__sync_synchronize(); // Not sure if this is overkill
obj = val;
return *this;
}
Run Code Online (Sandbox Code Playgroud)
您希望防止重新排序什么?大多数情况下你想这样写:
obj = val;
__sync_synchronize();
Run Code Online (Sandbox Code Playgroud)
反而。因为您希望确保从函数返回后该值已被写入。
归档时间: |
|
查看次数: |
5051 次 |
最近记录: |