为什么 `std::this_thread::yield()` 比 `std::this_thread::sleep_for(0s)` 慢 10 倍?

mat*_*tch 4 c++ linux multithreading scheduler

刚刚测试了两个小程序,

#include <thread>

int main()
{
    for (int i = 0; i < 10000000; i++)
    {
        std::this_thread::yield();
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

和:

#include <thread>
#include <chrono>

int main()
{
    using namespace std::literals;

    for (int i = 0; i < 10000000; i++)
    {
        std::this_thread::sleep_for(0s);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我在我的系统上得到了各自的计时(Ubuntu 22.04 LTS,内核版本 5.19.0-43-generic),

./a.out  0,33s user 1,36s system 99% cpu 1,687 total
Run Code Online (Sandbox Code Playgroud)

和:

./a.out  0,14s user 0,00s system 99% cpu 0,148 total
Run Code Online (Sandbox Code Playgroud)

为什么std::this_thread::yield()比 慢 10 倍std::this_thread::sleep_for(0s)

注意 g++ 和 clang++ 之间的计时类似。

编辑:正如答案中所指出的,这是 STL 实现的优化,调用sleep(0)实际上慢了 300 倍(50us vs 150ns)。

sba*_*bbi 8

快速浏览一下代码this_thread::sleep_for

template<typename _Rep, typename _Period>
inline void
sleep_for(const chrono::duration<_Rep, _Period>& __rtime)
{
    if (__rtime <= __rtime.zero())
      return;
    ...
Run Code Online (Sandbox Code Playgroud)

所以sleep_for(0s)什么也不做,事实上你的测试程序使用了0.0s的系统时间,基本上是一个完全在用户空间运行的空循环(事实上我怀疑如果你使用优化进行编译,它将被完全删除

另一方面,yield调用* sched_yield又将schedule()在内核空间中调用,因此至少执行一些逻辑来检查是否有另一个线程需要调度。

我相信你的 0.33s 用户空间时间基本上是系统调用开销。

*实际上跳转到__libcpp_thread_yieldthen 调用的地方sched_yield,至少在 Linux 上是这样