c++std::async慢于sequential for循环



我正在尝试为自定义游戏引擎创建一个物理引擎。目前一切都很好,但当发动机必须处理大约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,并且函数返回的future存储在物理引擎类的成员向量中,每次更新时都会清除该向量

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

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

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

感谢

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

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

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

如果你把几十万个更新分组到每个任务中,你可能会发现速度会提高,如果你没有足够的更新,就把它们作为一个任务来运行。

相关内容

  • 没有找到相关文章

最新更新