Да, это почти наверняка немного медленнее. Однако в большинстве случаев это не будет иметь значения, и стоимость будет перевешиваться преимуществом «логики и стиля».
Технически, функциональная локальная статическая переменная такая же, как глобальная переменная. Только то, что его имя не известно во всем мире (что хорошо), и его инициализация гарантированно произойдет не только в точно указанное время, но также только один раз и поточно-безопасно.
Это означает, что функциональная локальная статическая переменная должна знать, произошла ли инициализация, и, следовательно, требуется как минимум один дополнительный доступ к памяти и один условный переход, который не нужен глобальному (в принципе). Реализация может делать нечто подобное для глобалов, но для не требуется (и обычно этого не требуется).
Скорее всего, прыжок предсказан правильно во всех случаях, кроме двух. Первые два вызова, скорее всего, будут предсказаны как неправильные (обычно по умолчанию предполагается, что переходы выполнены, а не нет), неверное предположение при первом вызове, а последующие переходы предполагаются по тому же пути, что и последний, опять-таки неправильно). После этого вы должны быть готовы к 100% правильному прогнозу.
Но даже правильно спрогнозированный переход не является бесплатным (процессор все еще может запускать только определенное количество инструкций каждый цикл, даже если предположить, что для их завершения требуется ноль времени), но это не так много. Если задержка памяти, которая в худшем случае может составить пару сотен циклов, может быть успешно скрыта, то стоимость почти исчезает при конвейерной обработке. Кроме того, каждый доступ извлекает дополнительную строку кэша, которая в противном случае не потребовалась бы (вероятно, флаг инициализации был не сохранен в той же строке кэша, что и данные). Таким образом, у вас чуть хуже производительность L1 (L2 должен быть достаточно большим, чтобы вы могли сказать «да, ну и что»).
Он также должен действительно выполнить что-то один раз и поточно-ориентированно , чего не должен делать глобал (в принципе), по крайней мере, не так, как вы видите. Реализация может сделать что-то другое, но большинство просто инициализируют глобальные переменные до ввода main
, и нередко большая часть этого выполняется с memset
или неявно, потому что переменная хранится в сегменте, который все равно обнуляется.
Ваша статическая переменная должна быть инициализированной при выполнении кода инициализации, и это должно происходить потокобезопасным образом. В зависимости от того, сколько ваша реализация отстой, это может быть довольно дорого. Я решил отказаться от функции безопасности потоков и всегда компилировать с fno-threadsafe-statics
(даже если это не соответствует стандартам) после обнаружения того, что GCC (который в противном случае является универсальным компилятором OK) фактически блокирует мьютекс для каждой статической инициализации.