【问题标题】:c++ std::async slower then sequential for loopc++ std::async 比顺序 for 循环慢
【发布时间】:2020-08-04 11:22:31
【问题描述】:

我正在尝试为自定义游戏引擎创建物理引擎。目前一切正常,但是当引擎必须处理大约 4000 个物理体时,我遇到了一些性能问题。我很确定这不是渲染引擎的错,因为它使用实例渲染来实现粒子效果(我目前正在测试的女巫),并且如果它们都是静态的,则可以处理大约 200K 粒子。

到目前为止,一旦所有的碰撞都得到解决,我会通过施加重力并以那里的速度平移物体来更新场景中的所有物理物体

函数如下所示:

void mint::physics::PhysicsEngine::SymplecticEuler(mint::physics::PhysicsBody* body)
{
  mint::graphics::Entity *entity = body->GetEntity();

  // -- Symplectic Euler
  glm::vec2 gravity = glm::vec2(0.0f, (1.0f / core::Timer::Instance()->DeltaTime()) * 9.81f) * body->GravityScale();

  glm::vec2 dv = (body->Force() * body->GetMassData()->inv_mass + gravity * core::Timer::Instance()->DeltaTime());
  body->Velocity(body->Velocity() +  dv);

  glm::vec2 dxy = glm::vec2(body->Velocity() * core::Timer::Instance()->DeltaTime());
  entity->Translate(glm::vec3(dxy, 0.0f));
  // -- END -- Symplectic Euler

  // -- update the collider
  body->UpdateCollider();
  // -- END -- update the collider
}

这个函数将在每个物理体中运行一次,并像这样在 for 循环中调用

auto start = std::chrono::high_resolution_clock::now();
for (auto body : all_bodys)
{
    //SymplecticEuler(body);
    // -- using std::async
    fEulerFutures.push_back(std::async(std::launch::async, SymplecticEuler, body));
    //SymplecticEuler(body);
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> duration = end - start;
std::cout << "physics update took: " << duration.count() << std::endl;

我正在使用 std::chrono 来查看更新的时间,我有两种不同的方法来实现这一点,一种是调用SymplecticEuler(body),另一种方法是使用 std::async 并且从函数返回的未来存储在物理引擎类的成员向量中,每次更新都会清除一次

使用我编写的时序代码,顺序循环耗时 0.00014 秒,多线程循环耗时 0.005 秒。我不希望多线程循环比顺序循环花费更长的时间,但它确实如此我假设我使用 std::async 错误或在错误的上下文中使用它。我正在运行的程序正在运行一个包含 300 个粒子的简单粒子模拟,所以还没有什么大不了的。

如果我使用 std::async 是否正确,请告诉我,因为我对多线程的概念还是很陌生,或者我是否使用了太多线程来降低引擎的性能,或者我是否应该使用计算着色器而不是多线程(如果使用计算着色器会提高引擎的性能,请留下一些链接以获取有关如何在现代 openGL 中使用 c++ 计算着色器的教程)

这两个函数都是物理引擎类的成员,SymplecticEuler() 函数是静态函数

谢谢

【问题讨论】:

  • 你完成fEulerFutures.reserve(all_bodys.size())了吗?
  • 如果您使用 gcc 9(或更高版本)或 MSVC,您可以尝试使用并行执行策略:std::for_each(std::execution::par, all_bodys.begin(), all_bodys.end(), [](auto body) { SymplecticEuler(body); });

标签: c++ multithreading optimization compute-shader stdasync


【解决方案1】:

我不希望多线程循环比顺序循环花费更长的时间

我认为这就是你的问题所在,你为什么认为它会花更少的时间?将任务推送到并发数据结构的工作量(如果写得不好,可能涉及互斥锁,或者至少cmpxchg 指令),然后向内核同步对象发出信号(Windows 中的事件),并唤醒线程由内核线程调度程序作为响应,然后必须以线程安全的方式再次访问您的数据结构以删除任务 - 这是一项疯狂的工作。

多线程通常为 CPU(和库编写者)增加了更多工作,其好处是工作可以发生在其他线程上,让您的线程响应 GUI 事件而不是冻结。出于这个原因,您希望开销比您排队的工作量小几个数量级,而您的情况并非如此——您所拥有的只是一些 SIMD 指令。

如果您将数百/数千个更新分组到每个任务中,您可能会发现速度有所提高,如果您没有足够的更新,只需将它们作为一个任务运行即可。

【讨论】:

  • 我想使用多线程循环的原因是它会遍历所有对象并同时更新所有对象,而不是一次全部更新。我也这样做是为了了解有关多线程的更多信息。
  • 我理解,但正如我所说,以线程安全的方式对这些事物进行排队和出队的开销高于您首先排队的工作量。在大量工作中分批处理您的工作。
  • 使用计算着色器会更好还是工作量太大?
  • 你的意思是工作太少?因为真的,翻译和矢量添加实际上什么都不是!
  • 通过位置更新,它必须更新对象的模型矩阵,然后使用模型矩阵重新计算形状的世界位置,我不能像我一样在顶点着色器中这样做批量渲染。也许我应该考虑使用计算机着色器将矩阵计算转移到 gpu 上?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多