【发布时间】:2021-11-18 17:33:42
【问题描述】:
我已经为 C++ 开发了一个类似于 Java 的监视器对象,并进行了一些改进。主要的改进是不仅有一个用于锁定和解锁的自旋循环,还有一个用于等待事件的循环。在这种情况下,您不必锁定互斥体,而是在 wait_poll-function 上提供谓词,并且代码反复尝试锁定互斥体轮询,如果它可以锁定互斥体,则调用返回(或移动)对的谓词布尔型和结果类型。
在内核中等待信号量和/或事件对象 (Win32) 很容易花费 1.000 到 10.000 个时钟周期,即使调用立即返回,因为之前已设置信号量或事件。所以必须有一个与这个等待间隔有合理关系的旋转计数,f.e.旋转内核中花费的最小间隔的十分之一。
使用我的监视器对象,我从 glibc 中获取了自旋计数重新计算算法。而且我也在使用暂停指令。但我发现在我的 CPU(TR 3900X)上,暂停指令太快了。平均约为 0.78ns。在 Intel-CPU 上,大约 30ns 更合理。
这是代码:
#include <iostream>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <immintrin.h>
using namespace std;
using namespace chrono;
int main( int argc, char **argv )
{
static uint64_t const PAUSE_ROUNDS = 1'000'000'000;
auto start = high_resolution_clock::now();
for( uint64_t i = PAUSE_ROUNDS; i; --i )
_mm_pause();
double ns = (int64_t)duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count() / (double)PAUSE_ROUNDS;
cout << ns << endl;
}
为什么 AMD 采取了如此愚蠢的暂停时间? PAUSE 用于自旋等待循环,并且应该与缓存行内容翻转到不同核心并返回所需的时间紧密匹配。
【问题讨论】:
-
英特尔 CPU 上的暂停时间因代次而异(差异超过一个数量级),我们不能只说它是 30ns
-
Skylake之前的Intel只有5个时钟周期左右;在他们的实验发现可以提高吞吐量后,他们在 SKL 中将其提高到 100。 (特别是考虑与另一个超线程的竞争。)
-
应该与高速缓存行内容翻转到不同核心并返回所需的时间紧密匹配。 - 您可能想要节省一些功率(并避免 memory_order 机器核弹) )而不会等待太长时间,平均该间隔的一半(如果您假设丢失缓存行的时间是随机的,当然这可能不是高争用)。所以更短的停顿是有道理的。不过,没这么短;我同意只有几个时钟周期似乎有点愚蠢。
-
我猜你必须要求 AMD 解释他们所做的任何决定。 SO 无法为他们回答这个问题。
标签: assembly x86 amd-processor spinlock spinwait