如何解决std::thread 的运行速度比 std::future 慢很多
我有一些带有 Mainloop
的简单渲染程序,它在一个线程上以大约 8000 fps 的速度运行(除了绘制背景之外什么都不做),我想看看另一个线程渲染是否会在不更改的情况下扰乱当前上下文它(这并不出乎我的意料)。我在这里用这个简单的代码实现了这一点,
m_Thread = std::thread(Mainloop);
m_Thread.join();
这里的代码以某种方式运行得非常慢,大约 30 FPS。我觉得这很奇怪,我记得在另一个项目中我出于类似的基于性能的原因使用了 std::future
。所以我然后使用以下代码尝试使用 std::future
:
m_Future = std::async(std::launch::async,Mainloop);
m_Future.get();
这比单线程性能 (~7900) fps 低一点点。为什么 std::thread
比 std::future
慢这么多?
编辑:
忽略上面的代码,这里是一个最小的可重现示例,只需将 THREAD
切换为 0
或 1
进行比较:
#include <future>
#include <chrono>
#include <Windows.h>
#include <iostream>
#include <string>
#define THREAD 1
static void Function()
{
}
int main()
{
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::Now();
std::chrono::high_resolution_clock::time_point finish = std::chrono::high_resolution_clock::Now();
long double difference = 0;
long long unsigned int fps = 0;
#if THREAD
std::thread worker;
#else
std::future<void> worker;
#endif
while (true)
{
//FPS
finish = std::chrono::high_resolution_clock::Now();
difference = std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start).count();
difference = difference / 1000000000;
if (difference > 0.1) {
start = std::chrono::high_resolution_clock::Now();
std::wstring fpsstr = L"Fps: ";
fpsstr += std::to_wstring(fps);
SetConsoleTitle(fpsstr.c_str());
fps = 0;
}
#if THREAD
worker = std::thread(Function);
worker.join();
#else
worker = std::async(std::launch::async,Function);
worker.get();
#endif
fps += 10;
}
return 0;
}
解决方法
std::async
可以通过不同的方式实现。例如,可以有一个预先分配的线程池,每次在循环中使用 std::async
时,您只需重用池中的一个“热”线程。
每次使用 std::thread
时都会创建一个新的系统线程对象。与重用池中的线程相比,这可能是一笔巨大的开销。
我建议您在多线程环境中测试您的代码,在这种环境中,std::async
可能会开始争夺预先分配的系统对象。
在某些版本的 MSVC C++ 标准库中,std::async
从(系统)线程池中提取,而 std::thread
不提取。这可能会导致问题,因为我过去已经用尽它并陷入僵局。这也意味着随意使用会更快。
我的建议是在 std::thread
之上编写自己的线程池并使用它。您可以完全控制您有多少活动线程。
这是一个很难解决的问题,但依靠其他人解决它是行不通的,因为老实说,我使用的标准库实现并不能可靠地解决它。
请注意,在 N 大小的线程池中,大小为 N 的阻塞依赖链会死锁。如果您将线程数设为 CPU 数,并且不可靠地重用调用线程,您会发现在 4 核以上机器上测试的多线程代码经常在 2 核机器上死锁。
同时,如果你为每个任务创建一个线程池,并且它们堆叠起来,你最终会导致 CPU 抖动。
请注意,标准对于您实际可以运行的线程数量非常模糊。虽然 std async 必须表现得“好像”你创建了一个新的 std 线程,但实际上这意味着它们必须重新初始化和销毁任何 thread_local
对象。
标准中有最终的进度保证,但我在实际实现中看到使用 std::async
时违反了这些保证。所以我现在避免直接使用它。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。