Я разрабатываю игровой движок, который в Windows примерно в 3-4 раза медленнее, чем в Linux.
Я попытался профилировать приложение и не вижу особой проблемы, за исключением того, что в Windows все работает медленнее.
Я извлек очень небольшую часть приложения. Я компилирую его с помощью g++ -O3 perf.cpp и выполняю так: a.exe 500000000. Вот результаты:
Исходный код:
#include <iostream>
#include <cmath>
#include <vector>
#include <chrono>
struct Vector{
float X, Y, Z;
Vector(float X, float Y, float Z) : X(X), Y(Y), Z(Z){}
Vector vector(const Vector &target) const{
return Vector(target.X - X, target.Y - Y, target.Z - Z);
}
float dotProduct(const Vector &v) const{
return (X*v.X + Y*v.Y + Z*v.Z);
}
};
float compute(const std::vector<Vector> &v){
Vector vec1 = v[0].vector(v[2]);
Vector vec2 = v[1].vector(v[0]);
return vec1.dotProduct(vec2);
}
int main(int argc, char *argv[]){
unsigned int loopMax = atoi(argv[1]);
Vector va(1.5f, 3.0f, 8.0f*loopMax);
Vector vb(1.2f, 2.3f, 11.0f*loopMax);
Vector vc(8.2f, 5.0f, 12.0f*loopMax);
auto frameStartTime = std::chrono::high_resolution_clock::now();
float res = 0.0f;
for(unsigned int i=0; i<loopMax; ++i)
{
res += compute({va, vb, vc});
}
auto frameEndTime = std::chrono::high_resolution_clock::now();
auto diffTimeMicroSeconds = std::chrono::duration_cast<std::chrono::microseconds>(frameEndTime - frameStartTime).count();
std::cout<<"Time: "<<diffTimeMicroSeconds / 1000000.0 <<" sec, res: "<<res<<std::endl;
return 0;
}
Я знаю, что разница для одной итерации смехотворна (<1 мкс), но в итоге мое приложение работает в 3-4 раза медленнее.
Чем можно объяснить такую разницу? Как найти проблему?
Вы пробовали другой компилятор в Windows? Я имею в виду, что gcc довольно хорош, но в Windows компилятор Visual Studio лучше довольно часто ... может также попробовать clang.
В цикле есть потенциальное выделение кучи для аргумента std::vector<Vector> для compute. Это могло вызвать зависимость производительности от реализации ОС / стандартной библиотеки.
@eukaryota, это, вероятно, так - распределение кучи гарантировано, только clang может выполнять удаление кучи в настоящее время AFAIK.
Пожалуйста, не ссылайтесь на код. Укажите соответствующий код в вопроса, как текст.
Как насчет -march=native -flto?
Спасибо за ответы. Протестировано с "-march = native -flto": без разницы. Протестировано с лязгом: немного хуже (31 сек).





Попробуйте построить аргумент для compute один раз вне цикла. Если компилятор не исключает создание аргумента std::vector<Vector>, это, скорее всего, вызовет выделение кучи:
std::vector<Vector> arg{va, vb, vc};
for(unsigned int i=0; i<loopMax; ++i)
{
res += compute(arg);
}
Если есть выделение кучи, это, скорее всего, займет гораздо больше времени, чем остальное содержимое цикла. Продолжительность выделения кучи может сильно различаться в зависимости от системы и реализации. Его удаление, вероятно, значительно улучшит производительность в обоих случаях.
Если есть необходимость конструировать вектор каждый раз в вашем фактическом коде, вам следует рассмотреть возможность использования массива фиксированного размера (необработанный массив или std :: array), который будет размещаться не в куче, а в стеке, что намного Быстрее. Это кажется применимым, потому что вы используете в своей реализации compute ровно три элемента.
Если вы не знаете длину вектора во время компиляции, а также необходимо восстанавливать его каждый раз в горячем цикле, тогда вы может быть можете повторно использовать пространство, выделенное кучей, тем более, если вы можете указать хорошее предположение для максимальной длины:
std::vector<Vector> arg;
arg.reserve(1000); // Allocate for up to 1000 element
for(unsigned int i=0; i<loopMax; ++i)
{
arg.clear();
arg.push_back(va);
[...]
arg.push_back(vn);
res += compute(arg);
}
В самом деле, я не говорю, что код сильно оптимизирован. Я уже пытаюсь передать методу 3 аргумента вместо вектора, и разница огромна. Меня беспокоит разница во времени выполнения между Linux и Windows. Может ли в Windows выделение памяти в 3 раза медленнее? Это кажется мне огромным. Что вы думаете?
@OlivierMartin Как уже было сказано, трудно сказать, не видя сгенерированной сборки. Компилятор также может нести ответственность за разницу. Не знаю, разумна ли разница в 3 раза, но меня это не слишком удивляет. Фактически вы просто сравниваете вызов распределения. Что вы имеете в виду под already try to pass 3 arguments to method instead of vector? Вы по-прежнему выделяете память в куче с помощью compute({va, vb, vc}), потому что это сокращение для создания нового аргумента типа параметра функции с заданным списком инициализаторов.
Я имею в виду, что я изменил сигнатуру метода на «compute (const Vector & v0, const Vector & v1, const Vector & v2)» и вызов метода с «compute (va, vb, vc)». Я постараюсь получить сборку (никогда не делайте этого раньше), а также попробую другой компилятор. Спасибо за ваш ответ.
@OlivierMartin В этом случае распределение кучи должно быть удалено, и я ожидал бы, что результаты будут намного более похожими. Если это все еще не так, опубликуйте новый код вместе со сборкой.
Без выделения кучи результаты аналогичны. Я не ожидал, что распределение между ОС так сильно различается! Я проверю все свое приложение и гарантирую, что медленное выделение может объяснить время выполнения, которое у меня есть. Спасибо.
Возможно, вы используете антивирус только в Windows. Они перехватывают системные вызовы и замедляют работу ваших приложений. Я обнаружил, что разница между запуском с включенным / выключенным антивирусом незначительна, когда почти нет распределения динамической памяти (нет системных вызовов), но намного медленнее, когда их много.
Сгенерированная сборка для обоих случаев может быть полезна. Также вы должны вставить код в свой вопрос в виде текста.