c ++ std :: asyn c медленнее, чем последовательно для цикла - PullRequest
2 голосов
/ 21 апреля 2020

Я пытаюсь создать физический движок для пользовательского игрового движка. На данный момент все работает нормально, однако у меня возникают некоторые проблемы с производительностью, когда двигатель должен иметь дело примерно с 4000 физическими телами. Я совершенно уверен, что это не ошибка движка рендеринга, поскольку он использует рендеринг экземпляров для эффектов частиц (ведь я сейчас тестирую) и может обрабатывать около 200К частиц, если они все статичны c.

до сих пор, как только все столкновения были разрешены, я обновляю все физические тела в сцене, применяя силу тяжести и переводя тела на их скорость

функция выглядит следующим образом:

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
}

эта функция будет запускаться один раз для каждого физического тела и вызывается для l oop, например, так:

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 :: asyn c, и это будущее, возвращаемое функцией, сохраняется в векторе-члене Класс физического движка очищается один раз при каждом обновлении

с использованием написанного мною временного кода: l oop 0.00014 с, а многопоточность l oop - 0,005 с. Я бы не ожидал, что многопоточность l oop займет больше времени, чем последовательная l oop, но это так, я предполагаю, что я либо использую std :: asyn c неправильно, либо использую его в неправильном контексте. Программа, в которой я запускаю эту программу, запускает простую симуляцию частиц с 300 частицами, так что пока ничего большого.

Может кто-нибудь сообщить мне, правильно ли я использую std :: asyn c, потому что я все еще очень плохо знаком с концепцией многопоточности или использую слишком много потоков, чтобы снизить производительность движок или если мне следует использовать вычислительные шейдеры вместо многопоточности (если использование вычислительных шейдеров улучшило бы производительность движка, пожалуйста, оставьте несколько ссылок на руководства по использованию вычислительных шейдеров в современном openGL с c ++)

оба эти функции являются членами класса физического движка, а функция SymplecticEuler() является функцией * c

Спасибо

1 Ответ

3 голосов
/ 21 апреля 2020

Я бы не ожидал, что многопоточная l oop займет больше времени, чем последовательная l oop

Я думаю, что это ваша проблема, почему вы думаете, что это займет Меньше? Объем работы, выполняемой pu sh задачами для параллельной структуры данных (которая, вероятно, включает в себя мьютексы, если они записаны плохо, или, по крайней мере, cmpxchg инструкции в противном случае), а затем сигнализирует объект синхронизации ядра (событие в Windows), и в ответ на то, что планировщик потока ядра разбудил поток, который затем должен снова обратиться к вашей структуре данных, чтобы безопасно выполнить задачу, - это безумный объем работы.

Многопоточность в целом добавляет гораздо больше работы для ЦП (и для авторов библиотек), выгода в том, что работа может выполняться в других потоках, оставляя ваш поток реагировать на события GUI вместо зависания. По этой причине вы хотите, чтобы накладные расходы были на порядки меньше, чем объем работы, которую вы ставите в очередь, и это не так для вас - все, что у вас есть, - это несколько SIMD-инструкций.

Вы можете обнаружить увеличение скорости, если сгруппировать несколько сотен / тысяч таких обновлений для каждой задачи, и если их у вас недостаточно, просто запустите их все как задачу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...