Добрый день!
Перед тем как задам вопрос напомню, что статические локальные переменные это практически глобальные переменные, только у них ограничена область видимости. Их использование очень удобно например потому, что многие переменные (особенно в олимпиадном программировании) хочется называть одинаково. Статические локальные переменные позволяют например в каждой функции объявить свой массив cur и с одной стороны это будет глобальная переменная (ей выделится из статическая память), а с другой стороны ее не будет видно вне функции.
Вопрос заключается в следующем: есть какие-нибудь существенные минусы static-переменных? Например медленная работа, медленное выделение памяти и т.д. Мне просто вспоминается как я однажды избавлялся от них из-за каких-то проблем и не могу вспомнить из-за каких.
Заранее спасибо всем кто отпишется.
P.S.: картиночку добавил, чтобы пост смотрелся лучше)
Upd: только, что обнаружил что если объявить большой массив static local, то код медленно компилится (g++). С global быстрее.
В шахматном программированию рекомендуют по возможности использовать static переменные, так как они работают быстрее. Из минусов — они статические, находятся в конкретном месте памяти (а не в стеке), поэтому при рекурсивных вызовах сохраняют значение. Медленней работать будет только если выгодней использовать эту переменную как регистровую, и компилятор это понимает.
Это автосгенерированный ответ на вопрос?
В смысле? Этот ответ на вопрос про static локальные переменные. И кто постоянно минусует? Мне казалось что в приличном обществе давать советы можно независимо от цвета штанов. Видимо я ошибался, и пользователи фиолетового цвета права голоса не имеют. Не заслужили.
Сорри, может это я совсем не в теме, но мне ваш ответ напомнил вот это: автоматически сгенерированный текст
В шахматном программированию
Что такое шахматное программирование?
так как они работают быстрее
За счёт чего они работают быстрее?
поэтому при рекурсивных вызовах сохраняют значение
Почему нас особо волнуют рекурсивные вызовы?
Медленней работать будет только если выгодней использовать эту переменную как регистровую, и компилятор это понимает
Что такое регистровая переменная? Когда их выгодней использовать? Почему когда компилятор что-то понимает, то становится медленней? И собственно что становится медленней?
P.S. Автор топика задал очень интересный для меня вопрос и мне действительно хотелось бы в нём разобраться.
Что такое шахматное программирование? Шахматное программирование — написание шахматных программ, chess engine. Сила шахматной программы сильно зависит от производительности (оптимизаций). Вдобавок раньше прибавку силы от увеличения скорости в шахматных программах заметно преувеличивали, и компиляторы не умели так хорошо оптимизировать, видимо по-этому в советах по оптимизации кода шахматных программ советовали по возможности использовать static.
За счёт чего они работают быстрее? Первая попавшаяся ссылка http://stackoverflow.com/questions/10525707/can-i-get-best-performance-making-static-variables ~~~~~ In typical implementations, the version with static will just put the string somewhere in memory at compile time, whereas the version without static will make the function (each time it's called) allocate some space on the stack and write the string into that space.
The version with static, therefore,
is likely to be quicker may use less memory will use less stack space (which on some systems is a scarce resource) will play nicer with the cache (which isn't likely to be a big deal for a small string, but might be if foo is something bigger). ~~~~~
Почему нас особо волнуют рекурсивные вызовы?
Чтоб не напортачить.
Что такое регистровая переменная? Когда их выгодней использовать? Почему когда компилятор что-то понимает, то становится медленней? И собственно что становится медленней?
Если компилятор видит static, то переменной (простых типов) будет выделено место в памяти, и оптимизация по хранению её в регистре проводиться не будет. Если компилятор тупой, то вероятность что он грамотно раскидает переменные по регистрам процессора весьма мала. Если умный — то соответственно без static возможностей по оптимизации у него больше.
На счет быстроты. Меня в первую очередь интересует сравнение времени работы static local vs global. Понятно, что static local работает быстрее, чем local.
Не советую считать, что если у нас есть переменная, то мы к ней дописываем
static
и магически получаем прирост скорости. Наоборот, чрезмерное использованиеstatic
только мешает оптимизации, как ns_serg уже написал.Лучше думать так, без всяких ухищрений:
static
static
EDIT: Пожалуй, стоит добавить и такие пункты:
int
→ определяюconst
¹static const
¹ На самом деле, с современными компиляторами производительность не поменяется, даже если не написать
const
.Я именно так и понимаю. static local я хочу использовать как раз для больших массивов.
осталось не ясным, почему в этом списке нет вопросов
Если определить глобальный статический и локальный статический массив из
int
, скомпилировать его с помощью GCC в Linux и посмотреть на сгенерированный ассемблерный код, то получаем следующее:Справка по директивам ассемблера здесь: http://sourceware.org/binutils/docs-2.23.1/as/Pseudo-Ops.html#Pseudo-Ops
Однако далее, в объектном файле, оба этих массива превращаются просто в области в секции
.bss
. Если же инициализировать массивы (arr = { 1, 2, 3}
), то оба массива таким же одинаковым образом попадают в секцию.data
, и оба занимают одинаковое количество места в объектном файле.Получается, никаких различий между этими двумя массивами нет. В Windows/MinGW имеем то же самое, только вместо
.local
+.comm
используется директива.lcomm
.Если же вместо массивов взять собственный класс с конструктором, то становится интереснее. Из ассемблерного кода видно, что для глобального статического объекта в секцию
.init_array
дописывается адрес конструктора, который, видимо, будет вызван один раз при запуске программы. А для локального статического объекта происходит следующее: при каждом вызове функции производится проверка, не был ли ещё объект инициализирован, и если нет, то вызывается конструктор. Причём проверка не совсем простая и включает в себя вызовы функций__cxa_guard_acquire
и__cxa_guard_release
.То есть, для нетривиальных классов (non-POD) получается, что локальная статическая переменная замедляет выполнение программы по сравнению с глобальной статической переменной.
Вывод: использовать локальные статические переменные следует только тогда, когда их тип либо встроенный, например,
int
, либо POD-класс (то есть, какstruct
в C).Круто спасибо за объяснение. Только я не понял в связи с чем отличается время компиляции?
Скорей всего особенность компилятора. Локальные static от глобальных переменных практически ничем не отличаются. Кроме области видимости. Ну и разброс результатов. Возможно повторная компиляция покажет другой результат.
На массиве long double [3003][3003] время компиляции глобального массива меньше 1 секунды. У static local больше 9 секунд. Строчка компиляции:
Попробовал. Первая компиляция — 5s. Повторная 500 ms. Такая-же скорость как и глобальный массив. Попробуй скомпилировать повторно. У меня скорость компиляции плавает от 300 мс. до 5 секунд.
Upd: ох ты кажется я понял в чем проблема. Ужас вообще exe-файл первого варианта весит 640Kb, второго 104Mb. И как тут можно говорить, что это почти одно и то же?????????? Может они работают одинаково, но это совсем разные вещи.
Global. Стабильно меньше 1 секунды.
Static local. Стабильно больше 9 секунд.
52 кб, с -static 4Мб Просто чудеса :)
Я кажется все понял. Это из-за того, что я написал inline. Я в ужасе inline всегда казался таким безобидным))
Непонятно, с чем это связано, но… стоит убрать
inline
изsolve()
, и проблема исчезает! С GCC 4.8 на Linux повторить не удаётся (то есть, в обоих случаях работает правильно).В ассемблерном коде с
inline
видна директива.space 108216108
, безinline
массив вроде бы вообще удаляется оптимизатором.Да я только, что это заметил. У меня просто с каких-то пор руки инстинктивно пишут inline. Очень странно.
Upd: у меня g++ 4.6.2.
Кстати, я сейчас вспомнил, что у gen, тоже на MinGW, то ли раз, то ли два было что-то типа такого: программа с
main()
иsolve()
стабильно получает TLE. Берём и содержимоеsolve()
вставляем вmain()
,solve()
убираем — и… программа проходит.С этими
solve()
действительно происходит что-то непонятное.В моем варианте точно не удаляется, скорость выполнения и компиляции — одинакова копейка-в-копейку.
Скорее — точно удаляется. (Надо компилировать с включённым
-O2
.)5 секунд выполнения и 106.5 Мб выделенной памяти согласно диспетчера задач — значит массив точно есть. С включенным -O2
Под Ubuntu с g++ 4.5.2 exe-файл маленький. Т.е. все норм.
Ну вот, так и оказалось — фича конкретного компилятора при некотором стечении обстоятельств.
Вот еще мнение Криса Касперски о локальных массивах. http://www.xakep.ru/magazine/xa/117/116/1.asp
А здесь что не в порядке? Программист создает законный двухмерный массив, инициализируя малую его часть (очевидно, что остальные ячейки предполагается заполнить по ходу выполнения функции foo). Согласно Стандарту, здесь инициализируется весь массив, причем, ненулевые ячейки компиляторы инициализируют индивидуально, расходуя на каждую из них, по меньшей мере, одну машинную инструкцию. Это в идеале, а на практике компилятору MS VC необходимо 27 команд, чтобы справиться с вышеприведенным массивом. Хорошего мало, особенно, если функция foo вызывается больше одного раза. Стек не резиновый и обычно (читай — по умолчанию) потоку достается порядка 1 Мб.
За бездумное размещение массивов в стеке давно уже пора расстреливать. Ключевое слово "static", размещенное перед "int matrix", сокращает потребности в памяти и увеличивает скорость выполнения программы в несколько раз! А как быть, если статический массив нас «ну никак не устраивает»? Допустим, массив должен инициализироваться при каждом вхождении в функцию. Нет ничего проще! Размещаем исходный массив в глобальной или статической переменной, а при каждом вхождении в функцию копируем его во временный буфер, выделяемый из пула динамической памяти. Копирование, осуществляемое посредством memcpy, намного быстрее поэлементной инициализации (напоминаю, что статические массивы инициализируются на стадии компиляции, не транжиря процессорное время).
Почему пост минусуют? Здесь что-то неверно написано?
Я наверху написал почему :) Мне нравится как пишет Касперски. Он и в асме силен, и отлично понимает слабые и сильные стороны компиляторов. И если Касперски говорит что локальные не static массивы это плохо, то скорей всего так оно и есть. У него только в конце статьи небольшая ошибка. Он хотел сделать массив на четыре элемента, а вышло у него на три.
У Касперского стиль изложение материала слегка понятней, последовательней и обоснованней чем у тебя :)
Спасибо за подробное объяснение наверху!
В пол-второго ночи, после рабочего дня — тяжело последовательно излагать свои мысли )