Эффект производительности глобальных переменных (c, c ++) - PullRequest
14 голосов
/ 06 марта 2011

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

Теперь вопрос: я заметил, что некоторые очень часто используемые переменные и массивы помещаются в «Global» или «static».локальный »(что делает то же самое), есть ощутимый выигрыш в производительности (в диапазоне + 10%).Я пытаюсь понять, почему, и найти решение по этому поводу, так как я бы предпочел избегать использования этих типов распределения.Обратите внимание, что я не думаю, что разница заключается в «распределении», поскольку выделение нескольких переменных и небольшого массива в стеке происходит практически мгновенно.Я считаю, что разница заключается в «доступе» и «изменении» данных.

В этом поиске я нашел этот старый пост из stackoverflow: производительность C ++ глобальных переменных

Но я очень разочарован ответами там.Очень мало объяснений, в основном разглагольствования о «ты не должен этого делать» (эй, это не вопрос!) И очень грубые утверждения, такие как «это не влияет на производительность», что, очевидно, неверно, так как я измеряю это с точнымBenchmark Tools.

Как уже было сказано выше, я ищу объяснение и, если оно существует, решение этой проблемы.До сих пор у меня было ощущение, что вычисление адреса в памяти локальной (динамической) переменной стоит немного больше, чем глобальная (или локальная статическая).Может быть, что-то вроде разницы операций ДОБАВИТЬ.Но это не помогает найти решение ...

Ответы [ 4 ]

7 голосов
/ 06 марта 2011

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

Во многих случаях глобальная переменная имеет фиксированное смещение.Это позволяет сгенерированным инструкциям просто использовать этот адрес напрямую.(Что-то вроде строки MOV AX,[MyVar].)

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

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

7 голосов
/ 06 марта 2011

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

2 голосов
/ 06 марта 2011

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

Но если вы ищете скорость, Ваш пример в комментарии while(p<end)stats[*p++]++; является очевидным кандидатом на развертывание, например:

static int stats[M];
static int index_array[N];
int *p = index_array, *pend = p+N;
// ... initialize the arrays ...
while (p < pend-8){
  stats[p[0]]++;
  stats[p[1]]++;
  stats[p[2]]++;
  stats[p[3]]++;
  stats[p[4]]++;
  stats[p[5]]++;
  stats[p[6]]++;
  stats[p[7]]++;
  p += 8;
}
while(p<pend) stats[*p++]++;

Не рассчитывайте, что компилятор сделает это за вас. Он может или не может понять это.

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

1 голос
/ 06 марта 2011

Если у вас есть что-то вроде

int stats[256]; while (p<end) stats[*p++]++;

static int stats[256]; while (p<end) stats[*p++]++;

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

static int stats[256] = { 0 }; while (p<end) stats[*p++]++;

Таким образом, чтобы быть честным, сначала прочитайте

 int stats[256] = { 0 }; while (p<end) stats[*p++]++;

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

Теперь может быть преимущество времени выполнения static, так как инициализация выполняется во время компиляции (или запуска программы).

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

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

...