Multithreading LWARX和STWCX的x86等效物

Multithreading LWARX和STWCX的x86等效物,multithreading,x86,low-level,reference-counting,Multithreading,X86,Low Level,Reference Counting,我正在寻找LWARX和STWCX的等价物(在PowerPC处理器上可以找到)或在x86平台上实现类似功能的方法。此外,哪里是了解此类信息的最佳地点(例如,用于无锁/无等待编程的好文章/网站/论坛) 编辑 我想我可能需要提供更多细节,因为假设我只是在寻找一个CAS(比较和交换)操作。我想做的是实现一个无锁引用计数系统,它带有智能指针,可以被多个线程访问和更改。我基本上需要一种在x86处理器上实现以下功能的方法 int* IncrementAndRetrieve(int **ptr) { in

我正在寻找LWARX和STWCX的等价物(在PowerPC处理器上可以找到)或在x86平台上实现类似功能的方法。此外,哪里是了解此类信息的最佳地点(例如,用于无锁/无等待编程的好文章/网站/论坛)


编辑
我想我可能需要提供更多细节,因为假设我只是在寻找一个CAS(比较和交换)操作。我想做的是实现一个无锁引用计数系统,它带有智能指针,可以被多个线程访问和更改。我基本上需要一种在x86处理器上实现以下功能的方法

int* IncrementAndRetrieve(int **ptr) { int val; int *pval; do { // fetch the pointer to the value pval = *ptr; // if its NULL, then just return NULL, the smart pointer // will then become NULL as well if(pval == NULL) return NULL; // Grab the reference count val = lwarx(pval); // make sure the pointer we grabbed the value from // is still the same one referred to by 'ptr' if(pval != *ptr) continue; // Increment the reference count via 'stwcx' if any other threads // have done anything that could potentially break then it should // fail and try again } while(!stwcx(pval, val + 1)); return pval; } 整数*递增和递减(整数**ptr) { int-val; int*pval; 做 { //获取指向该值的指针 pval=*ptr; //如果为NULL,则只返回NULL,即智能指针 //然后也将变为空 if(pval==NULL) 返回NULL; //获取引用计数 val=lwarx(pval); //确保我们从中获取值的指针 //仍然是“ptr”所指的同一个 如果(pval!=*ptr) 继续; //如果有其他线程,则通过“stwcx”增加引用计数 //如果你做了任何有可能破坏它的事情 //失败后再试一次 }而(!stwcx(pval,val+1)); 返回pval; } 我真的需要一些相当准确地模仿LWARX和STWCX的东西来实现这一点(我无法找到一种方法来使用我目前为x86找到的CompareExchange、swap或add函数实现这一点)


谢谢,x86不像PPC那样直接支持“乐观并发”——相反,x86对并发的支持是基于“锁前缀”,请参阅。(一些所谓的“原子”指令,例如XCHG,实际上是通过内在地断言锁前缀来获得原子性的,不管汇编代码程序员是否真的编写了锁前缀)。从外交角度讲,它不完全是“防炸弹的”(事实上,我可以说,它很容易发生事故;-)。

您可能正在寻找cmpxchg系列指令

您需要在这些操作之前使用一个lock指令来获得等效的行为

快速浏览一下可用的内容

int* IncrementAndRetrieve(int **ptr)
{
  int val;
  int *unpacked;
  do
  {   
    val = *ptr;
    unpacked = unpack(val);

    if(unpacked == NULL)
      return NULL;
    // pointer is on the bottom
  } while(!cas(unpacked, val, val + 1));
  return unpacked;
}
你可能会得到类似的结果:

mov ecx,dword ptr [esp+4]
mov edx,dword ptr [esp+8]
mov eax,dword ptr [esp+12]
lock cmpxchg dword ptr [ecx],edx
ret 12
你应该读

编辑


在回答更新的问题时,您是否希望做类似的事情?如果是这样,请查看该代码和该目录中的文件-它们肯定会让您开始学习。

正如Michael提到的,您可能要查找的是
cmpxchg
指令

需要指出的是,实现这一点的PPC方法称为(LL/SC),而x86体系结构使用(CAS)。LL/SC比CAS具有更强的语义,因为对条件地址处的值的任何更改都将导致存储失败,即使其他更改将该值替换为负载所条件的相同值。另一方面,CAS在这种情况下会成功。这就是众所周知的ABA问题(有关更多信息,请参阅CAS链接)


如果您需要x86体系结构上更强大的语义,可以使用x86_64下的x86s双宽度比较和交换(DWCAS)指令
cmpxchg8b
,或
cmpxchg16b
来近似实现。这允许您一次以原子方式交换两个连续的“自然大小”单词,而不仅仅是通常的一个。基本思想是两个单词中的一个包含感兴趣的值,另一个包含始终递增的“突变计数”。虽然从技术上讲,这并不能消除这个问题,但变异计数器在两次尝试之间缠绕的可能性非常低,因此对于大多数目的来说,它是一个合理的替代品。

您正在尝试的操作不会按您期望的方式工作。您可以使用InterlockedIncrement函数(Win32函数;程序集:XADD)实现上述功能


您的代码没有执行您认为它可以执行的操作的原因是,另一个线程仍然可以在第二次读取*ptr和stwcx之间更改值,而不会使stwcx无效。

如果您是64位的,并且限制自己说1tb的堆,则可以将计数器打包到24个未使用的顶部位。如果您有字对齐指针,则底部的5位也可用

int* IncrementAndRetrieve(int **ptr)
{
  int val;
  int *unpacked;
  do
  {   
    val = *ptr;
    unpacked = unpack(val);

    if(unpacked == NULL)
      return NULL;
    // pointer is on the bottom
  } while(!cas(unpacked, val, val + 1));
  return unpacked;
}

不知道LWARX和STWCX是否会使整个缓存线无效,CAS和DCA会。这意味着,除非你愿意扔掉大量内存(每个独立的“可锁定”指针64字节),否则如果你真的将软件推向压力,你将看不到有多大的改进。到目前为止,我所看到的最好的结果是,人们有意识地对64b进行加密,围绕64b规划他们的结构(打包不会引起争议的东西),将所有东西都集中在64b边界上,并使用显式读写数据屏障。缓存线失效可能需要大约20到100个周期,这使得它成为一个更大的实际性能问题,而不仅仅是锁避免


此外,您必须计划不同的内存分配策略来管理受控泄漏(如果您可以将代码划分为逻辑“请求处理”-一个请求“泄漏”,然后在最后释放其所有内存块)或datailed分配管理,以便处于争用状态的一个结构永远不会接收由相同结构/集合的元素重新分配的内存(以防止ABA)。其中一些可能非常违反直觉,但要么如此,要么为GC付出代价。

这两个链接非常好(实际上几天前偶然发现了这两个页面),但不幸的是,这不是我想要的(我更新了问题以更好地反映这一点)DCAS看起来几乎正确,除了我需要更改一个单词,只有当指向该单词的指针在执行此操作时没有更改时(这有点令人困惑,希望问题的更新有助于澄清这一点)。我设法找到了解决方法