Подтвердить что ты не робот

В C, использует ли статические переменные в функции быстрее?

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

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

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

4b9b3361

Ответ 1

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

Кроме того, локальные переменные, вероятно, быстрее из-за локализации кэша.

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


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

Локальные переменные обычно имеют как временную, так и пространственную локальность (они получают это за счет создания в стеке). Кроме того, они могут "распределяться" непосредственно на регистры и никогда не записываться в память.

Ответ 2

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

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

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

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

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

Ответ 3

Абсолютно нет! Единственная разница в производительности - это когда инициализируются переменные

    int anint = 42;
 vs
    static int anint = 42;

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

Однако разница настолько тривиальна, что она едва заметна. Это распространенное заблуждение, что хранилище должно выделяться для "автоматических" переменных при каждом вызове. Это не так, что C использует уже выделенное пространство в стеке для этих переменных.

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Это усугубляется, когда вы вводите указатели, например, у вас есть следующий код:

int myFunction()
{
    SomeStruct *A, *B;
    FillOutSomeStruct(B);
    memcpy(A, B, sizeof(A);
    return A.result;
}

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

http://en.wikipedia.org/wiki/Pointer_alias

Ответ 6

Да, использование статических переменных сделает функцию немного быстрее. Однако это вызовет проблемы, если вы когда-либо захотите сделать свою программу многопоточной. Поскольку статические переменные распределяются между вызовами функций, одновременное использование функции в разных потоках приведет к поведению undefined. Многопоточность - это тот тип вещей, который вы, возможно, захотите сделать в будущем, чтобы действительно ускорить ваш код.

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

Как и предложения M2tM, запуск профилировщика также является хорошей идеей. Проверьте gprof на тот, который довольно прост в использовании.

Ответ 7

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

Функции

C создают фрейм стека, в который помещаются переданные параметры, а также локальные переменные, а также возвращаемый указатель обратно туда, где вызывающий вызывал функцию. Здесь нет распределения памяти. Обычно это простое движение указателя, и это все. Доступ к данным со стека также довольно быстрый. Штрафы обычно вступают в игру, когда вы имеете дело с указателями.

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

Основное различие между вашими сценариями - это объем памяти, а не столько скорость.

Ответ 8

Использование статических переменных может сделать ваш код значительно медленнее. Статические переменные должны существовать в области данных "данных". Чтобы использовать эту переменную, функция должна выполнить команду загрузки для чтения из основной памяти или инструкцию сохранения для ее записи. Если этот регион не находится в кеше, вы теряете много циклов. Локальная переменная, которая живет в стеке, наверняка имеет адрес, который находится в кеше, и может даже быть в регистре процессора, никогда не появляясь в памяти.

Ответ 9

Я согласен с остальными комментариями о профилировании, чтобы узнать такие вещи, но, вообще говоря, функция статических переменных должна быть медленнее. Если вы хотите их, то, что вы действительно на самом деле, является глобальным. Функция statics вставляет код/​​данные, чтобы проверить, была ли вещь уже инициализирована, которая запускается каждый раз, когда вы вызываете вашу функцию.

Ответ 10

Профилирование может не увидеть разницу, разобрать и узнать, что искать может.

Я подозреваю, что вы получите только вариацию, равную нескольким тактам за цикл (в среднем в зависимости от компилятора и т.д.). Иногда изменение будет драматичным улучшением или значительно медленнее, и это не обязательно будет связано с тем, что переменная home переместилась в/из стека. Допустим, вы сохранили четыре тактовых цикла для вызова функции для 10000 вызовов на 2-х процессорном процессоре. Очень грубый расчет: 20 микросекунд сохранены. 20 микросекунд много или немного по сравнению с текущим временем выполнения?

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

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