c ++ для вопроса оптимизации цикла - PullRequest
6 голосов
/ 25 февраля 2011

У меня есть следующий код в VC ++:

for (int i = (a - 1) * b; i < a * b && i < someObject->someFunction(); i++)
{
    // ...
}

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

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

int start = (a - 1) * b;
int expra = a * b;
int exprb = someObject->someFunction();
for (int i = startl i < expra && i < exprb; i++)
{
    // ...
}

Ответы [ 6 ]

7 голосов
/ 25 февраля 2011

Краткий ответ: это зависит. Если компилятор может выводить, что выполнение someObject->someFunction() каждый раз и кэширование результата один раз приводит к одинаковым эффектам, это разрешается (но не гарантируется). Возможен ли этот статический анализ, зависит от вашей программы: в частности, что такое статический тип someObject и каков его динамический тип, а также что на самом деле делает someFunction(), является ли он virtual, и так далее на.

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

int start = (a - 1) * b;
int expra = a * b;
int exprb = someObject->someFunction();
for (int i = start; i < expra && i < exprb; i++)
    // ...

Или, если вы хотите быть кратким:

for (int i = (a - 1) * b, expra = a * b, exprb = someObject->someFunction();
     i < expra && i < exprb; i++)
    // ...
6 голосов
/ 25 февраля 2011

По моему опыту, компилятор VC ++ не будет оптимизировать вызов функции, если он не увидит реализацию функции в момент компиляции вызывающего кода.Поэтому перенос вызова за пределы цикла - хорошая идея.

5 голосов
/ 25 февраля 2011

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

В вашем примере назначение переменных для этих простых арифметических выражений ничего не меняет по отношению к производимому объектному коду и, на мой взгляд, делает код менее читабельным. Если у вас нет набора длинных выражений, которые не могут быть разумно помещены в одну или две строки, вам следует избегать использования временных переменных - если ни по какой другой причине, то просто для уменьшения загрязнения пространства имен.

Использование временных переменных подразумевает значительные накладные расходы на управление для программиста, чтобы разделить их и избежать непреднамеренных побочных эффектов. Это также затрудняет повторное использование фрагментов кода.

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

Лично я бы пошел с этим:

int expr = someObject->someFunction();
for (int i = (a - 1) * b; i < a * b && i < expr; i++)
{
    // ...
}
1 голос
/ 25 февраля 2011

Компилятор не может делать никаких предположений относительно того, будет ли ваша функция возвращать одно и то же значение каждый раз.Давайте представим, что ваш объект является сокетом, как мог компилятор знать, что будет его выводом?

Кроме того, оптимизация, которую может выполнить компилятор в таких циклах, сильно зависит от того, объявлены ли a и b как const или нет, и от того, являются ли они локальными.С помощью продвинутых схем оптимизации можно сделать вывод, что a и b не модифицируются ни в цикле, ни в вашей функции (опять же, вы можете представить, что ваш объект содержит некоторую ссылку на них).

Хорошо, вЕсли коротко: перейдите на вторую версию вашего кода!

0 голосов
/ 25 февраля 2011

По сути, вы можете спросить, будет ли компилятор включать функцию someFunction () и будет ли он видеть, что someObject является одним и тем же экземпляром в каждом цикле, и если он это сделает, он потенциально «кэширует» возвращаемое значение не переоценивайте его.

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

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

Просто сделайте это и не полагайтесь на компилятор:

for (int i = (a - 1) * b, iMax = someObject->someFunction();
       i < a * b && i < iMax; ++i) 
{
  // body
}
0 голосов
/ 25 февраля 2011

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

Если вас интересует читабельность кода, как насчет использования:

int maxindex = min (expra, exprb);
for (i=start; i<maxindex; i++)

ИМХО, длинные строки не улучшают читабельность.

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

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