忙轮询std :: atomic-msvc优化了循环-为什么以及如何防止?

时间:2018-11-29 11:14:43

标签: c++ multithreading visual-c++ atomic stdatomic

我正在尝试实现一个简单的忙循环功能。

这应该继续轮询std :: atomic变量最大次数(spinCount),如果状态在给定的尝试内确实发生了变化(变为NOT_AVAILABLE以外的任何值),则返回true;否则返回false:

// noinline is just to be able to inspect the resulting ASM a bit easier - in final code, this function SHOULD be inlined!
__declspec(noinline) static bool trySpinWait(std::atomic<Status>* statusPtr, const int spinCount)
{
    int iSpinCount = 0;
    while (++iSpinCount < spinCount && statusPtr->load() == Status::NOT_AVAILABLE);
    return iSpinCount == spinCount;
}

但是,似乎MSVC只是优化了Win64发行版上的循环。我对Assembly很不好,但是对我来说却根本不像尝试读取statusPtr的值一样:

int iSpinCount = 0;
000000013F7E2040  xor         eax,eax  
    while (++iSpinCount < spinCount && statusPtr->load() == Status::NOT_AVAILABLE);
000000013F7E2042  inc         eax  
000000013F7E2044  cmp         eax,edx  
000000013F7E2046  jge         trySpinWait+12h (013F7E2052h)  
000000013F7E2048  mov         r8d,dword ptr [rcx]  
000000013F7E204B  test        r8d,r8d  
000000013F7E204E  je          trySpinWait+2h (013F7E2042h)  
    return iSpinCount == spinCount;
000000013F7E2050  cmp         eax,edx  
000000013F7E2052  sete        al  

我的印象是,带有std :: memory_order_sequential_cst的std :: atomic创建了一个编译器屏障,该屏障应防止出现这种情况,但事实并非如此(或者,我的理解可能是错误的)。

这是我在做什么错,或更确切地说,如何在不优化循环的情况下最好地实现该循环,而对整体性能的影响却最小?

我知道我可以使用#pragmaoptimize(“”,off),但是(除了上面的示例中),在我的最终代码中,出于性能原因,我非常希望将此调用内联到更大的函数中。似乎这个#pragma通常会阻止内联。

感谢任何想法!

谢谢

1 个答案:

答案 0 :(得分:4)

  

但在我看来,它甚至根本都没有尝试读取statusPtr的值

它会在循环的每次迭代中重新加载它:

000000013F7E2048  mov         r8d,dword ptr [rcx] # rcx is statusPtr
  

我的印象是std::atomicstd::memory_order_sequential_cst会产生一个编译器障碍,应该防止出现这种情况,

这里您只需要std::memory_order_relaxed就可以了,因为线程之间仅共享一个变量(甚至更多,此代码也不会更改atomic变量的值)。没有重新排序的问题。

换句话说,此功能可以正常工作。

您可能想使用PAUSE指令,请参见Benefitting Power and Performance Sleep Loops