1

我已经为 C++ 开发了一个类似于 Java 的监视器对象,并进行了一些改进。主要的改进是不仅有一个用于锁定和解锁的自旋循环,还有一个用于等待事件的循环。在这种情况下,您不必锁定互斥体,而是在 wait_poll-function 上提供谓词,并且代码反复尝试锁定互斥体轮询,如果它可以锁定互斥体,则调用返回(或移动)对的谓词布尔型和结果类型。

等待内核中的信号量和/或事件对象 (Win32) 可以轻松地从 1.000 到 10.000 个时钟周期,即使调用立即返回,因为之前已设置信号量或事件。所以必须有一个与这个等待间隔有合理关系的旋转计数,fe 旋转内核中花费的最小间隔的十分之一。

使用我的监视器对象,我从 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 用于自旋等待循环,并且应该与高速缓存行内容翻转到不同核心并返回所需的时间非常匹配。

4

1 回答 1

5

但我发现在我的 CPU(TR 3900X)上,暂停指令太快了。平均约为 0.78ns。在 Intel-CPU 上,大约 30ns 更合理。

pause指令与时间没有任何关系,也不打算用作时间延迟。

这样做pause是为了防止 CPU 浪费其资源(推测性地)并行执行循环的许多迭代;这在内核中的不同逻辑处理器可以使用这些资源的超线程情况下特别有用,但对于改善条件更改时退出循环所需的时间也很有用(因为您没有“N次迭代”在条件改变之前排队的指令)。

鉴于这种; 对于可能同时运行 200 条指令的极其复杂的 CPU,pause它本身可能会立即发生,但会导致“200 个周期长”的流水线气泡;对于一个极其简单的 CPU(“按顺序”,没有推测执行)pause可能/应该什么都不做(被视为 a nop)。

PAUSE 用于自旋等待循环,并且应该与高速缓存行内容翻转到不同核心并返回所需的时间非常匹配。

不。假设缓存线在不同 CPU 的缓存中处于“修改”状态,并且之后的指令pause类似于“ cmp [lock],0”,导致 CPU 尝试将缓存线置于“共享”状态。pause在尝试将高速缓存行置于“共享”状态之后,CPU 应该无缘无故地浪费时间多长时间?

注意:如果您确实需要一点时间延迟,那么您需要查看umwait说明。不过,您不需要时间延迟-您想要超时(例如“旋转pause; 直到rdtsc表示经过了一定的时间)。为此,我很想将其分解为内部循环”pause并检查条件 N 次”,然后是执行“如果时间限制尚未到期,则重试内部循环”的外部循环。

于 2021-09-26T12:17:48.590 回答