Накладные расходы на объявление переменной внутри функции, выполняемой в цикле? - PullRequest
0 голосов
/ 09 декабря 2011

Был задан этот вопрос уже. Ответ был «Пространство стека для локальных переменных обычно выделяется в области действия функции». и поэтому нет никакой разницы в накладных расходах при объявлении переменных вне / внутри цикла.

Теперь представьте, что у нас есть фрагмент с функцией внутри цикла:

void do_sth(int &i) {int var=i+1;}
int i = 0;
while(i < 100)
{
    do_sth(i);
    i++;
}

И второй фрагмент с объявленной снаружи переменной:

int i = 0;
int var;
while(i < 100)
{
    var = i+1;
    i++;
}

Мой вопрос - каковы издержки в случае первого фрагмента в практическом сценарии (с современным компилятором)? Если действительно есть накладные расходы, то насколько они велики? Сравнимо ли это с, скажем, дополнительным сложением (оператор +) с целыми числами на каждом шаге цикла?

Ответы [ 8 ]

2 голосов
/ 09 декабря 2011

Современный компилятор с включенной оптимизацией, скорее всего, встроит вызов функции при условии, что он соответствует встроенным требованиям (без внешней связи и т. Д.), И поэтому две версии будут генерировать идентичный код.* Если функция не встроенная, то действительно есть издержки: вызов функции и возвращение функции с аргументом, передаваемым в стеке.Это немного больше, чем простое дополнение.

1 голос
/ 09 декабря 2011

Лучший способ выяснить это - посмотреть на дизассемблированный код в отладчике.В этом случае первый код накладывается на вызов функции в цикле.В зависимости от соглашения о вызове, используемого для функции, для вызова функции могут происходить разные вещи.Обычный сценарий в ассемблерном коде - это передача аргумента в стек, вызов функции, затем функция создает кадр стека, извлекает стек для получения параметра, а при возврате функции выталкивает стек, чтобы получить кадр стека вызывающей стороны.Для этого кода, тело функции которого очень короткое, накладные расходы могут быть примерно в 10 раз больше фактического тела функции.(10 инструкций против 1 инструкции).Если вы определяете функцию как встроенную функцию, все накладные расходы уходят.

0 голосов
/ 09 декабря 2011

Что я хотел бы знать - какой процент от общего времени выполнения программы тратится в этом цикле?

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

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

0 голосов
/ 09 декабря 2011

Вы уверены, что правильно сформулировали проблему / вопрос? Из того, что вы опубликовали - функция do_sth будет иметь обычные издержки при вызове функции. Если вы включите его, то накладные расходы исчезнут.

0 голосов
/ 09 декабря 2011

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

Если тело вашего do_sth видно при компиляции цикла в вашем примере, компилятор, скорее всего, встроит его, а затем удалит присвоение var (и выделение стека для var) какмертвый код, поэтому для этого сценария накладные расходы на самом деле не существуют.Если do_sth не может быть встроено, стоимость вызова функции более важна, чем объявление типа int.И в случае, если функция может быть встроена, есть явная возможность компилятора преобразовать первую версию во вторую, даже если var не мертвый код.Так что для таких примеров это действительно не имеет значения.

Может иметь значение, если ваша переменная является более сложным типом (не POD classtype).В этом случае первая версия будет вызывать конструктор и деструктор один раз для каждой итерации, а вторая будет вызывать оператор присваивания один раз для каждой итерации, а конструктор и деструктор - только один раз.Однако обратите внимание, что здесь ничего не сказано о том, какая версия быстрее (зависит от реализации этих методов).

0 голосов
/ 09 декабря 2011
int i = 0;
int var;
while(i < 100)
{
    var = i+1;
    i++;
}

int i = 0;
while(i < 100)
{
    int var = i+1;
    i++;
}

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

0 голосов
/ 09 декабря 2011

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

0 голосов
/ 09 декабря 2011

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

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