Эффективность циклов, написанных вручную, по сравнению с перегрузками операторов

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

В ходе написания моего кода у меня возник соблазн просто свернуть свой собственный класс Vector с простыми арифметическими перегрузками (+, -, * /), чтобы я мог упростить такие операторы, как:

// old:
for (int i = 0; i < 3; i++)
    r[i] = r1[i] - r2[i];

// new:
r = r1 - r2;

Который должен быть более или менее идентичен в сгенерированном коде. Но когда дело доходит до более сложных вещей, может ли это сильно повлиять на мою производительность? Один пример, который у меня есть в моем коде, таков:

Версия, написанная вручную:

for (int j = 0; j < 3; j++)
{
    p.vel[j] = p.oldVel[j] + (p.oldAcc[j] + p.acc[j]) * dt2 + (p.oldJerk[j] - p.jerk[j]) * dt12;
    p.pos[j] = p.oldPos[j] + (p.oldVel[j] + p.vel[j]) * dt2 + (p.oldAcc[j] - p.acc[j]) * dt12;
}

Использование класса Vector с перегрузками операторов:

p.vel = p.oldVel + (p.oldAcc + p.acc) * dt2 + (p.oldJerk - p.jerk) * dt12;
p.pos = p.oldPos + (p.oldVel + p.vel) * dt2 + (p.oldAcc - p.acc) * dt12;

Я пытаюсь оптимизировать свой код для повышения скорости, поскольку такой код выполняется внутри внутренних циклов. Повлияет ли использование перегруженных операторов для этих целей на производительность? Я выполняю численное интегрирование системы из n взаимно тяготеющих тел. Эти векторные операции чрезвычайно распространены, поэтому важно, чтобы они выполнялись быстро.

Любое понимание будет оценено, как и любые идиомы или уловки, о которых я не знаю.


person Mike Bailey    schedule 22.04.2010    source источник
comment
Возможно, вы захотите изучить шаблоны выражений. Но имейте в виду, что для получения точных ответов вам потребуется профилировать любые решения, которые вы пробуете.   -  person GManNickG    schedule 22.04.2010
comment
Кроме того, некоторые компиляторы не только встраиваются для вас, но и разворачивают короткие циклы, которые имеют определенное количество выполнений (например, 3). Вы можете посмотреть на дизассемблирование и убедиться, что это правда, или вы можете сделать то, что предложили другие, и провести некоторые тесты, чтобы увидеть, намного ли быстрее перегрузка операторов.   -  person Xavier Ho    schedule 22.04.2010
comment
Xavier, я скомпилировал перегруженный оператор и вручную написанную версию вместе со сборкой + исходными файлами .asm. Большинство петель были очень похожи. У некоторых, таких как тот, который я разместил выше, были очень длинные цепочки сборки для перегрузки оператора и гораздо более короткие цепочки с моими ручными циклами. Тем не менее, как я прокомментировал ниже, моя версия Vector была еще быстрее.   -  person Mike Bailey    schedule 22.04.2010
comment
Вы должны пометить свой вопрос тегом C++.   -  person Vicente Botet Escriba    schedule 25.04.2010


Ответы (3)


Если операции встраиваются и оптимизируются вашим компилятором, вы обычно не должны видеть никакой разницы между хорошим написанием кода (с использованием операторов, чтобы сделать его читабельным и удобным для сопровождения) и ручным встраиванием всего.

Ручное встраивание также значительно увеличивает риск ошибок, потому что вы не будете повторно использовать один кусок хорошо протестированного кода, вы будете писать один и тот же код снова и снова. Я бы рекомендовал писать код с помощью операторов, а затем если вы можете доказать, что можете ускорить его, встраивая вручную, дублировать код и вручную встраивать вторую версию. Затем вы можете сравнить два варианта кода друг с другом, чтобы доказать (а) эффективность ручного встраивания и (б) что читабельный код и код, встраиваемый вручную, дают одинаковый результат.

Однако, прежде чем вы начнете встраивать вручную, у вас есть простой способ ответить на свой вопрос для себя: написать несколько простых тестовых случаев в обоих направлениях, затем выполнить несколько миллионов итераций и посмотреть, какой подход выполняется быстрее. Это научит вас многому о том, что происходит, и даст вам определенный ответ для вашей конкретной реализации и компилятора, который вы никогда не получите из теоретических ответов, которые вы получите здесь.

person Jason Williams    schedule 22.04.2010
comment
Спасибо, я пошел дальше и замусорил свой код некоторыми #ifdefs, чтобы проверить версии, написанные вручную, и версии оператора. Код на основе Vector был на самом деле немного быстрее (на несколько сотен миллисекунд быстрее). Немного по сравнению с несколькими минутами работы, но это, безусловно, что-то значительное. - person Mike Bailey; 22.04.2010

Я хотел бы посмотреть на это с другой стороны; начиная с класса Vector, и если у вас возникнут проблемы с производительностью, вы можете увидеть, быстрее ли ручное встраивание вычислений.

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

person Guffa    schedule 22.04.2010

Ознакомьтесь с примерами кода ConCRT

http://code.msdn.microsoft.com/concrtextras/Release/ProjectReleases.aspx?ReleaseId=4270

Есть пара (включая образец NBody), которые делают кучу таких трюков с векторными типами и шаблонами и т. д.

person Ade Miller    schedule 29.04.2010