繁体   English   中英

关于__sync_synchronize()的c ++ 98中的C ++ 0x原子实现问题

[英]C++0x atomic implementation in c++98 question about __sync_synchronize()

我编写了followin原子模板,以模仿即将到来的c ++ 0x标准中可用的原子操作。

但是,我不确定是否需要返回基础值的__sync_synchronize()调用。

根据我的理解,__sync_synchronize()是一个完整的内存屏障,我不确定在返回对象值时我需要这么昂贵的调用。

我很确定在值的设置周围需要它,但我也可以用程序集来实现它。

__asm__ __volatile__ ( "rep;nop": : :"memory" );

有谁知道我确实需要在返回对象时使用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 );
    }

};

更新:由于编译器优化,我希望确保操作在读/写重新排序方面是一致的。

首先,一些小小的评论:

volatile T obj;

volatile在这里毫无用处,更是你自己制造了所有障碍。

inline T operator++( int )

内联是不必要的,因为在类中定义方法时暗示了它。

吸气剂和二传手:

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

为了确保读写时内存访问的总排序,每次访问都需要两个障碍(如下所示)。 我很满意只有障碍(II)和(III),因为它们足以满足我提出的一些用途(例如指针/布尔说数据就在那里,自旋锁),但是,除非另有说明,否则我不会省略其他,因为有人可能需要它们(如果有人表示你可以在不限制可能的用途的情况下省略一些障碍,那将会很好,但我不认为这是可能的)。

当然,这将是不必要的复杂和缓慢。

也就是说,我会抛弃障碍,甚至在类似模板的任何地方使用障碍的想法。 注意:

  • 该接口的排序语义全部由您定义; 如果您认为界面在这里或那里有障碍,它们必须在这里或那里,期间。 如果您没有定义它,您可以提出更有效的设计,因为并非所有障碍,甚至不是完全障碍,都可能需要特定问题。
  • 通常,如果你有一个可以提供性能优势的无锁算法,你就会使用原子; 这意味着过早地使访问失望的接口可能无法用作它的构建块,因为它会妨碍性能本身。
  • 无锁算法通常包含无法通过一种原子数据类型封装的通信,因此您需要知道算法中发生了什么,以便将障碍准确地放置在它们所属的位置(例如,在实施锁定时,您需要一个屏障 )我已经获得了它,但你发布它之前 ,这两个都是写,至少在原则上)
  • 如果你不想遇到问题,并且不确定在算法中明确地放置障碍,只需使用基于锁的算法。 这没什么不好的。

顺便说一句,c ++ 0x接口允许您指定精确的内存排序约束。

inline operator T()
{
    __sync_synchronize();   // Not sure this is overkill
    return obj;
}

简短版本:这有点过分。

长版:

为什么要将此类实现为模板? 它没有意义,因为只允许1-8字节的整数类型的原子操作,你甚至不能确定所有平台上都支持8字节整数。

您应该将原子类实现为非模板版本,并使用硬件/系统的“本机”整数类型。 这是32位处理器/ os上的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*));

BOOST_STATIC_ASSERT直接来自C ++ 0x的“static_assert()”。

如果您使用的是“完美拟合”整数​​类型,则可以像这样编写运算符:

operator native_int_type() { return obj; }

因为obj是易失性的,所以保证获取值而不返回任何缓存的值。 而且因为您使用的是“本机”整数类型,所以可以确保读取这样的值是原子的。

atomic& operator=( native_integer_type val )

同样,如果您使用正确的整数类型,则不需要同步。 读取/设置intel 32位系统上的int32是原子的,因此在64位系统上读取/设置int64。

我认为将atomic实现为模板没有任何好处。 原子操作取决于平台。 最好提供一个“atomic_int”类,它只保证至少有4个字节(如果你支持32位和64位系统)和一个“atomic_pointer”(如果你需要它)。 这样,类的名称也意味着语义和目的。

如果你只是使用“原子”而不是人们想到的:“哇,我只需要将我的字符串类放在这个模板中然后它就是线程安全的!”。


编辑:回答您的更新:“由于编译器优化,我希望确保操作在读/写重新排序方面是一致的。”

要防止编译器和cpu重新排序读/写操作,您需要__sync_synchronize()。

但请注意,获取/释放语义可能比完全障碍产生更好的性能。


EDIT2:

inline atomic< T > & operator=( T val )
{
    __sync_synchronize();   // Not sure if this is overkill
    obj = val;
    return *this;
}

你想要什么阻止重新排序? 在大多数情况下,你想写这个:

    obj = val;
    __sync_synchronize();

代替。 因为您希望确保在从函数返回后写入值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM