Performance 原子记忆建议

Performance 原子记忆建议,performance,scalability,atomic,memcpy,Performance,Scalability,Atomic,Memcpy,在测试程序的可伸缩性时,我遇到了这样一种情况:我必须将memcpy操作作为原子操作。我必须将64字节的数据从一个位置复制到另一个位置 我遇到了一个解决方案,那就是在变量上旋转: struct record{ volatile int startFlag; char data[64]; volatile int doneFlag; }; 下面是伪代码 struct record *node; if ( node->startFlag ==0 ) { // test


在测试程序的可伸缩性时,我遇到了这样一种情况:我必须将memcpy操作作为原子操作。我必须将64字节的数据从一个位置复制到另一个位置
我遇到了一个解决方案,那就是在变量上旋转:

struct record{
    volatile int startFlag;
    char data[64];
    volatile int doneFlag;
};
下面是伪代码

struct record *node;
if ( node->startFlag ==0 ) {  // testing the flag 
    if( CompareAndSwap(node->startFlag , 0 ,1 ) ) {  // all thread tries to set, only one will get success and perform memcpy operation 
        memcpy(destination,source,NoOfBytes);
        node->doneFlag = 1; // spinning variable for other thread, those failed in CompAndSwap 
    }
    else {
         while ( node->doneFlag==0 ) { // other thread spinning 
          ; // spin around and/or use back-off policy  
         }
   }}
这可以作为原子memcpy执行吗?虽然如果执行memcpy的线程被抢占(在memcpy之前或之后,但在设置doneFlag之前),那么其他线程将继续旋转。或者可以做些什么来让这个原子化
这种情况类似于其他线程必须等待,除非数据被复制,因为它们必须与插入的数据以及它们自己的数据进行比较。
我在startFlag的情况下使用测试、测试和设置方法来减少一些昂贵的原子操作。 自旋锁也是可伸缩的,但是我已经测量到原子调用比自旋锁提供更好的性能,而且我正在寻找这个代码段中可能出现的问题。 由于我使用自己的内存管理器,所以内存分配和空闲调用对我来说代价高昂,因此使用另一个缓冲区并在其中复制内容,然后设置指针(因为指针大小是在原子操作下)代价高昂,因为它将需要许多mem alloc和mem free调用


编辑我没有使用互斥,因为互斥看起来不可伸缩而且这只是程序的一部分,所以关键部分并不是那么小(我知道对于较大的关键部分,很难使用原子操作)

使用同步机制。互斥似乎是合理的


如果您担心可伸缩性,请尝试使用监视器。

您的代码片段肯定已损坏。在node->startFlag上有一场比赛

不幸的是,没有复制64字节的原子方法。我想你有很多选择

  • 以原子方式访问节点->startFlag。我已经写了两篇关于这个主题的帖子:和
  • 使用用户模式自旋锁保护整个东西
  • 使用类似于RCU的方法。你可以阅读有关RCU的文章。换句话说,就是使用指针引用要复制的缓冲区。然后你会:
  • 分配新的缓冲区
  • 创建它的内容(源代码中的memcpy)
  • 以原子方式用新的缓冲区替换缓冲区
  • 等待所有访问旧缓冲区的线程过期并释放它
  • 希望能有帮助。
    亚历克斯。

    不要使用锁,使用关键部分。锁的重量很重,关键部分的速度非常非常快(根据平台的不同,只有几条指令)。您没有指定操作系统,我在这里发布的信息在Windows中也有体验,尽管其他操作系统也应该类似

    您担心CriticalSection如果包含大量代码,其伸缩性可能不足以满足您的需要?根本原因(可能是您在哪里读到的)是,如果线程长时间保持CS,CriticalSection不能在多个线程中交错。您可以通过只围绕真正需要原子化的代码部分包装CS来避免这种情况。另一方面:如果您使用的CS粒度太细,那么百分比开销当然会增加。这是任何同步都无法避免的权衡


    您说您需要的原子操作是64字节的副本:在这种情况下,您与CS的同步开销可以忽略不计。试试看。通过同步的粒度(大约64字节的单个副本或大约4个副本),您可以通过做一些实验来平衡线程交错粒度与百分比开销。但总的来说:CS的速度足够快,可扩展性也足够好。

    现在已经很晚了,但对于其他人来说,下面的步骤更简单、更快,对缓存的压力也更小

    注意:我将CAS更改为GCC中相应的原子内置项。不需要“易失性”,CAS引入了内存屏障

    // Simpler structure
    struct record {
        int spin = 0;
        char data[64];
    };
    
    
    
    struct record *node;
    
    while (node->spin || ! __sync_bool_compare_and_swap(&node->spin , 0 , 1)); // spin
    memcpy(destination,source,NoOfBytes);
    node->spin = 0; 
    

    PS:我不确定CAS而不是node->spin=0是否可以提高效率。

    为什么不使用适当的锁/互斥锁?为什么要使用HTML格式化源代码?你没有看到编辑器正上方与格式化相关的按钮吗?@phresnel:现在看到了,谢谢如果你想序列化线程,你无法逃脱阻塞。根据可伸缩性,我考虑的是单个互斥锁在每个线程中需要添加的行数。原子操作只能处理少量数据,通常只有一个寄存器。除此之外,如果您想要一个更长大小的原子op,您可以在汇编中使用
    锁(不是针对每个程序员)。如果数据较长,普通用户空间程序将使用同步机制。同意,为指针大小的内存位置提供了原子操作,但也有双原子(通常称为DCA)和扩展的方法,即Word软件事务内存和对象软件事务内存(尽管未被广泛接受)。所以我在寻找原子memcpy,因为要复制的数据很小(64字节)。是的,STM方法避免锁定。但是在这种情况下,仅仅为了一个成员而使用它们是一件痛苦的事情。那么你怎么看?什么可以使它具有可伸缩性?你能评论一下我展示的代码吗?它似乎是无锁的。同时删除您的旧评论,让我们继续讨论,因为SO不允许在这里进行聊天/讨论(一对一)。有什么答案吗?实际上我维护了自己的内存管理器,所以调用内存分配和释放内存调用的成本很高。所以我不能用你提到的第三个。你们能评论一下我的方法吗,我已经在代码片段中展示过了。是的,在startFlag上有比赛,但这是一种测试、测试和设置的方式,我在这里检查