std::future::wait 应该使用这么多 CPU 吗?是否有性能更高的调用

Should std::future::wait be using so much CPU? Is there a more performant call?

本文关键字:是否 性能 调用 CPU wait future std      更新时间:2023-10-16

编辑: tl;dr -- 这个问题似乎仅限于一小部分操作系统/编译器/库组合,现在在 GCC Bugzilla 中被跟踪为 Bug 68921,这要归功于@JonathanWakely。

正在等待未来,我注意到top显示 100% 的 CPU 使用率,strace显示稳定的futex调用流:

...
[pid 15141] futex(0x9d19a24, FUTEX_WAIT, -2147483648, {4222429828, 3077922816}) = -1 EINVAL (Invalid argument)
...

这是在Linux 4.2.0(32位i686(上,使用gcc版本5.2.1编译。

这是我的最小可行示例程序:

#include <future>
#include <iostream>
#include <thread>
#include <unistd.h>
int main() {
  std::promise<void> p;
  auto f = p.get_future();
  std::thread t([&p](){
    std::cout << "Biding my time in a thread.n";
    sleep(10);
    p.set_value();
  });
  std::cout << "Waiting.n";
  f.wait();
  std::cout << "Done.n";
  t.join();
  return 0;
}

这是编译器调用(没有-g的相同行为(:

g++ --std=c++11 -Wall -g -o spin-wait spin-wait.cc -pthread

有没有性能更高的替代方案?

这是一个逻辑上相似的程序,使用 std::condition_variable 似乎性能更好:

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <unistd.h>
int main() {
  bool done = 0;
  std::mutex m;
  std::condition_variable cv;
  std::thread t([&m, &cv, &done](){
    std::cout << "Biding my time in a thread.n";
    sleep(10);
    {
      std::lock_guard<std::mutex> lock(m);
      done = 1;
    }
    cv.notify_all();
  });
  std::cout << "Waiting.n";
  {
    std::unique_lock<std::mutex> lock(m);
    cv.wait(lock, [&done]{ return done; });
  }
  std::cout << "Done.n";
  t.join();
  return 0;
}

我是否对基于 std::future 的代码做错了什么,或者我的libstdc++中的实现是否那么糟糕?

不,当然不应该这样做,这是实现中的一个错误,而不是std::future的属性。

现在 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68921 - 不断调用futex(2)的循环处于__atomic_futex_unsigned::_M_load_and_test_until

它看起来像是 syscall 函数的简单缺失参数,因此将垃圾指针传递给内核,内核抱怨它不是一个有效的timespec*参数。我正在测试修复程序,明天将提交,因此它将在GCC 5.4中修复

不,不应该。它通常效果很好。

(在评论中,我们试图确定更多关于特定损坏配置的信息,其中生成的可执行文件似乎在旋转等待,但我相信这就是答案。确定这是否仍然是最新 g++ 中 32 位目标的旋转等待仍然很好。

promise

是promise-future通信通道的"推送"端:在共享状态下存储值的操作与(如std::memory_order中所定义(从等待共享状态的任何函数(如std::future::get(的成功返回同步。

我认为这包括std::future::wait.

[ std::promise::set_value ] 以原子方式将值存储到共享状态中,并使状态准备就绪。 该操作的行为就像set_valueset_exceptionset_value_at_thread_exit,并且在更新承诺对象时set_exception_at_thread_exit获取与承诺对象关联的单个互斥锁。

虽然他们用承诺对象而不是共享状态来描述同步有点令人不安,但意图非常明确。

cppreference.com[*] 继续以在上述问题中不起作用的方式使用它。("这个例子展示了 promise 如何用作线程之间的信号。

相关文章: