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

Имеют ли глобальные переменные более быстрый код?

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

Было ли это правдиво, и если да, то это все еще верно сегодня?

4b9b3361

Ответ 1

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

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

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

Некоторые из советов - устаревшие годы - указатели FAR перестали быть проблемой на ПК давным-давно.

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

Вы можете либо вынуть assert полностью, или вы можете просто добавить #define NDEBUG при компиляции окончательной версии.

Мой совет вам, если вы действительно хотите оценить достоинства любого из этих 15 советов, а так как статье 14 лет, было бы скомпилировать код в современном компиляторе (см. Visual С++ 10) и попробовать для идентификации любой области, где использование глобальной переменной (или любой другой подсказки) сделает ее быстрее.

[Просто шутить - мой реальный совет состоял бы в том, чтобы полностью игнорировать эту статью и задавать вопросы о производительности конкретных в Stack Overflow, когда вы нажимаете на проблемы в своей работе, которые вы не можете решить. Таким образом, полученные вами ответы будут проверяться экспертами, поддерживаться примером кода или хорошими внешними доказательствами и текущими.]

Ответ 2

При переключении с параметров на глобальные переменные может произойти одна из трех:

  • он работает быстрее
  • он запускает те же
  • он работает медленнее

Вам нужно будет измерить производительность, чтобы увидеть, что быстрее в нетривиальном конкретном случае. Это было верно в 1996 году, сегодня это правда, и это правда завтра.

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

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

При анализе производительности глобальных переменных по сравнению с передачей параметров актуальным является способ, которым компилятор реализует их. Глобальные переменные обычно хранятся в фиксированных местах. Иногда компилятор генерирует прямую адресацию для доступа к глобальным переменным. Иногда, однако, компилятор использует еще одну косвенность и использует таблицу символов для глобальных переменных. IIRC gcc для AIX сделал это 15 лет назад. В этой среде глобалы малых типов всегда были медленнее, чем локальные и передаваемые параметры.

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

Ответ 3

Все уже дали соответствующие предостережения в отношении того, что это определенная платформа и программа, нужно на самом деле измерять тайминги и т.д. Таким образом, с учетом сказанного уже позвольте мне ответить на ваш вопрос напрямую по конкретному случаю игрового программирования на x86 и PowerPC.

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

Это больше не относится к x86 или PowerPC, используемому в большинстве игровых консолей. Использование глобальных переменных обычно медленнее, чем передача параметров по двум причинам:

  • Передача параметров реализована лучше. Современные процессоры передают свои параметры в регистры, поэтому чтение значения из списка параметров функции происходит быстрее, чем операция загрузки памяти. X86 использует привязку регистров и переадресацию хранилища, поэтому похоже, что перетасовка данных в стек и обратно может быть простым перемещением регистра.
  • Задержка кэширования данных намного превосходит тактовую частоту процессора в большинстве соображений производительности. Стек, который в значительной степени используется, почти всегда находится в кеше. Загрузка с произвольного глобального адреса может привести к промаху в кеше, что является огромным штрафом, поскольку контроллер памяти должен идти и извлекать данные из основной ОЗУ. ( "Огромный" здесь 600 циклов или более.)

Ответ 4

Что значит "быстрее"?

Я знаю, что понимание программы с глобальными переменными занимает у меня намного больше времени, чем без.

Если дополнительное время, затрачиваемое программистом (-ами) меньше времени, получаемого пользователями при запуске программы с помощью глобальных переменных, то я бы сказал, что использование global выполняется быстрее.

Но учтите, что программа будет работать 10 человек один раз в день в течение 2 лет. И что он занимает 2,84632 сек без глобалов и 2.84217 сек с глобалями (увеличение 0.00415 сек). Это на 727 секунд меньше TOTAL. Получение 10 минут времени работы не стоит введения глобального в отношении времени программиста.

Ответ 5

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

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

Ответ 6

Устраняя проблемы ремонтопригодности и правильности, в основном существуют два фактора, которые будут влиять на производительность в отношении глобальных переменных и параметров.

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

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

Все это просто добавляет до точки, сделанной плакатом над мной:

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

* it runs faster
* it runs the same
* it runs slower

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

В зависимости от конкретного поведения вашего точного компилятора и точных деталей аппаратного обеспечения, которое вы используете для запуска вашего кода, возможно, что в некоторых случаях глобальные переменные могут быть очень слабыми. Возможно, эта возможность стоит попробовать на каком-то коде, который работает слишком медленно, как эксперимент. Это, вероятно, не стоит посвящать себя, поскольку ответ на ваш эксперимент может измениться завтра. Итак, правильный ответ - это почти всегда идти с "правильными" шаблонами проектирования и избегать уродливого дизайна. Ищите лучшие алгоритмы, более эффективные структуры данных и т.д., Прежде чем намеренно пытаться спагеттизировать ваш проект. Гораздо лучше выигрыш в долгосрочной перспективе.

И, помимо аргумента time time time vs user time, я добавлю аргумент time time time time time to the Moore. Если вы предполагаете, что закон Мура сделает компьютеры примерно наполовину такими же быстрыми с каждым годом, то ради простого круглого числа мы можем предположить, что прогресс происходит в устойчивом 1% -ном прогрессе в неделю. ЕСЛИ вы смотрите на микрооптимизацию, которая может улучшить такие вещи, как 1%, и это добавит неделю к проекту из-за усложнения вещей, тогда просто удержание недели будет иметь тот же эффект в среднем времени выполнения для ваших пользователей.

Ответ 7

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

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

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

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

Для обсуждения проблем, присущих глобальным переменным, и некоторых способов их избежать, см. "Покс на глобалях" Джона Ганнсле. Статья относится к разработке встроенных систем, но в целом применима; это то, что некоторые разработчики встроенных систем считают, что у них есть все основания использовать глобальные переменные, возможно, по тем же причинам, которые были ошибочными, чтобы оправдать его в разработке игр.

Ответ 8

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

Итак, ребята, которые говорят, что это зависит, я думаю, что они просто ошибаются. Даже при передаче параметра REGISTER все еще будут БОЛЬШЕ циклов процессора и БОЛЬШИХ накладных расходов для нажатия параметров до вызываемого абонента.

ОДНАКО - Я никогда этого не делаю. Процессоры сейчас превосходят, и временами, когда это могло быть проблемой, было 12Mhz 8086s. В настоящее время, если вы не пишете встроенный или супер-турбозарядный код производительности, придерживайтесь того, что хорошо выглядит в коде, что не нарушает логику кода и процветает, чтобы быть модульным.

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

Ответ 9

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

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

Извините, нет хорошего ответа для такого общего вопроса, просто измерьте его (и попробуйте разные сценарии тоже)

Ответ 10

Это было быстрее, когда у нас были процессоры < 100 МГц. Теперь, когда эти процессоры в 100 раз быстрее, эта "проблема" на 100 раз меньше. Тогда это было неважно, это было очень важно, когда вы делали это в сборке и не имели (хорошего) оптимизатора.

Говорит парень, запрограммированный на 3-МГц процессоре. Да, вы читали, что права и 64k недостаточно.

Ответ 11

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

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

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

Ответ 12

Как и все остальное: да и нет. Нет никакого ответа, потому что это зависит от контекста.

контрапунктов:

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

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

В любом случае глобальное состояние - это просто плохая практика, это повышает сложность кода. Хорошо разработанный код, естественно, достаточно быстрый 99,9% времени. Похоже, что новые языки удаляют глобальное состояние вместе. E удаляет глобальное состояние, поскольку оно нарушает их модель безопасности. Haskell удаляет состояние все вместе. Тот факт, что Haskell существует и имеет реализации, превосходящие большинство других языков, является достаточным доказательством для меня, что я больше никогда не буду использовать глобальные переменные.

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

Ответ 13

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

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

  • Минимизировать количество локальных переменных в функции до нескольких (явных) переменных регистра.
  • Свести к минимуму количество параметров функции, т.е. с помощью указателей на структуры вместо использования тех же параметров-созвездий в функциях, которые вызывают друг друга.
  • Сделайте функцию "голой", это означает, что она вообще не использует стек.
  • Используйте "правильные обратные вызовы" (не работает с java/-bytecode или java-/ecma- script)
  • Если нет лучшего способа, взломайте себя так, как TABLES_NEXT_TO_CODE, который находит ваши глобальные переменные рядом с кодом функции. В функциональных языках это бэкэнд-оптимизация, которая также использует указатель на функцию как указатель данных; но пока вы не программируете на функциональном языке, вам нужно только найти эти переменные рядом с теми, которые используются функцией. Опять же, вы хотите, чтобы это удаляло обработку стека из вашей функции. Если ваш компилятор генерирует код ассемблера, который обрабатывает стек, тогда нет смысла делать это, вместо этого вы можете использовать указатели.

Я нашел этот "обзор атрибутов gcc": http://www.ohse.de/uwe/articles/gcc-attributes.html

и я могу дать вам эти теги для googling: - Правильный хвостовой вызов (в основном это относится к императивным основам функциональных языков) - TABLES_NEXT_TO_CODE (в основном это относится к Haskell и LLVM)

Ответ 14

Но у вас есть "код спагетти", когда вы часто используете глобальные переменные.