Есть ли случаи, когда глобалы / синглтоны полезны при разработке игр? [закрыто]

11

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

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

Олафур Вааге
источник

Ответы:

30

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

JasonD
источник
Очень близко к моей мантре.
Олафур Вааге
15

Определенно не исчерпывающий список, но здесь идет:

Cons

  • Пожизненное управление . Синглеты и глобалы могут запускаться до инициализации ключевых систем (например, кучи), в зависимости от того, как вы их настроили. Если вам когда-нибудь захочется их разорвать (например, полезно, если вы отслеживаете утечки), вы должны быть осторожны с порядком разрыва или начать разбираться с вещами типа феникс-синглтонов . (См. Статический порядок инициализации фиаско .)
  • Контроль доступа . Должен ли рендеринг иметь только постоянный доступ к игровым данным, тогда как обновление становится неконстантным? Это может быть сложнее обеспечить с помощью синглетонов и глобалов.
  • Гос . Как отметил Кай, сложнее функционально разложить и преобразовать синглтоны, чтобы они вообще работали в архитектуре без разделяемой памяти. Это также может повлиять на производительность других типов систем NUMA (доступ к памяти не локальный). Синглтоны обычно представляют централизованное состояние и, таким образом, являются антитезой преобразований, которые делаются легче благодаря чистоте.

Либо за, либо против

  • Параллелизм . В параллельной среде синглтоны могут быть либо болью (вы должны учитывать проблемы гонки данных / повторного входа), либо благословением (проще централизовать и рассуждать о блокировке и управлении ресурсами). Умное использование таких вещей, как локальное хранилище потоков, может несколько смягчить потенциальные проблемы, но обычно это не простая проблема.
  • Codegen (в зависимости от компилятора, архитектуры и т. Д.): Для наивной одноэлементной реализации создания при первом использовании вы можете оценивать дополнительную условную ветвь для каждого доступа. (Это может сложить.) Несколько глобальных переменных, используемых в функции, могут привести к переполнению литерального пула . Подход "структура всех глобальных переменных" может сэкономить место в пуле литералов: только одна запись в основе структуры, а затем смещения, закодированные в инструкциях загрузки. Наконец, если вы избегаете глобальных и синглтонов любой ценой, вам, как правило, нужно использовать хотя бы немного дополнительной памяти (регистры, стек или кучу) для передачи указателей, ссылок или даже копий.

Pros

  • Простота . Если вы знаете, что перечисленные выше недостатки не так сильно на вас влияют (например, вы работаете в однопоточной среде для платформы с одним ЦП), вы избегаете некоторой архитектуры (например, вышеупомянутой передачи аргументов). Синглеты и глобалы могут быть более понятны для менее опытных программистов (хотя может быть проще использовать их неправильно).
  • Управление жизненным циклом (снова). Если вы используете «структуру глобалов» или какой-то другой механизм, отличный от запроса «создать по первому запросу», вы можете легко читать и детально контролировать порядок инициализации и уничтожения. Вы до некоторой степени автоматизируете это или управляете им вручную (управлять им может быть за или против, в зависимости от количества глобалов / синглетонов и их взаимозависимостей).

Мы часто используем «структуру глобальных синглетонов» в наших портативных устройствах. Названия ПК и консолей имеют тенденцию меньше полагаться на них; мы переключимся больше на архитектуру событий / обмена сообщениями. При этом заголовки ПК / консоли все еще часто используют центральный TextureManager; так как он обычно оборачивает один общий ресурс (память текстур), это имеет смысл для нас.

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

Leander
источник
Отличный список. Лично я нахожу только отрицательную ценность в синглетах и ​​глобалах из-за проблем, которые происходят из общего (вероятно, изменяемого) состояния. Я не считаю, что проблема «codegen» является проблемой; вам нужно либо передать указатели через регистры, либо вам нужно выполнить некоторую последовательность операций, чтобы получить их, я думаю, что это заметно меняет код и размер данных, но сумма будет примерно одинаковой.
Даш-Том-Бэнг
Да, что касается кода, единственное, что мы на самом деле видели, существенно изменило переход от отдельных глобальных таблиц к глобальной таблице: она сократила буквенные пулы и, следовательно, размер раздела .text, на несколько процентов. Что очень хорошо для небольших систем. =) Преимущества в производительности, связанные с тем, что литералы не так сильно ударили по dcache, были просто хорошим (хотя и крошечным в большинстве случаев) побочным преимуществом. Еще одно преимущество перехода к глобальной таблицы была возможность действительно легко переместить его в секцию , которая проживала в быстрой памяти ...
Leander
4

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

Кая
источник
2

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

Я часто использую глобальные переменные для вещей, к которым нужен доступ через двигатель. Мой инструмент производительности - один хороший пример, который я использую по всему движку для вызова. Звонки просты; ProbeRegister (), ProbeHit () и ProbeScoped (). Их реальный доступ немного сложнее и использует глобальные переменные для некоторых вещей.

Саймон
источник
2

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

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

Глобалы имеют свое место, также как и gotos, и их не следует выбрасывать из рук, а использовать их с осторожностью.

Это хорошее объяснение есть в Руководстве по стилю Google C ++

Kimau
источник
Я не согласен, что это главная проблема; «Не используйте глобальные переменные» восходит дальше, чем конструкторы существования. Я бы сказал, что с ними есть две основные проблемы. Во-первых, они усложняют рассуждения о программе. Если у меня есть функция, которая обращается к глобалу, поведение этой функции зависит не только от ее собственных аргументов, но и от всех других функций, которые обращаются к глобалу. Это намного больше, чтобы думать. Во-вторых, даже если вы можете держать все это в своей голове (вы не можете), они все равно приводят к более сложным проблемам параллелизма.
@ Джо, ключевое слово - «зависимости». Функция, которая обращается к глобальным переменным (или синглетам или любому другому общему состоянию), имеет неявные зависимости от всех этих вещей. Гораздо проще рассуждать о небольшом количестве кода, если все его зависимости явны, что вы получаете, когда полный список зависимостей задокументирован в списке аргументов.
Даш-Том-Бэнг
1

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

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

Kylotan
источник
1

Я склонен рекомендовать использовать какой-либо DI / IoC-контейнер с настраиваемым управлением временем жизни вместо синглетонов (даже если вы используете менеджер времени жизни «одного экземпляра»). По крайней мере, тогда легко заменить реализацию для облегчения тестирования.

Майк Штробель
источник
Для тех из вас, кто не знает, DI - это «Внедрение зависимостей», а IoC - «Инверсия контроля». Ссылка: martinfowler.com/articles/injection.html (я читал об этом раньше, но никогда не видел аббревиатуру, поэтому мне пришлось немного поискать.) +1 за упоминание этого превосходного преобразования, хотя.
Леандер
0

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

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

Что касается многопоточных проблем, упомянутых выше, было бы довольно просто предусмотрительно реализовать механизм блокировки ресурсов, которые могут совместно использоваться потоками. http://en.wikipedia.org/wiki/Read/write_lock_pattern

lathomas64
источник
0

Синглтоны - отличный способ сохранить общее состояние в раннем прототипе.

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

Например, в iOS вы используете синглеты для получения [UIApplication sharedApplication], в cocos2d вы можете использовать его для получения ссылок на определенные объекты, такие как [CCNotifications sharedManager], и лично я обычно начинаю с синглтона [Game sharedGame], где я могу состояние хранилища, которое совместно используется многими различными компонентами.

DFectuoso
источник
0

Вау, это интересно для меня, так как у меня никогда не было проблем с одиночной структурой. Мой текущий проект представляет собой игровой движок C ++ для Nintendo DS, и я реализую множество утилит аппаратного доступа (например, VRAM Banks, Wifi, два графических движка) в качестве единичных экземпляров, поскольку они предназначены для обертывания глобального C функции в базовой библиотеке.


источник
Мой вопрос был бы то, почему использование одиночек вообще? Я вижу, что людям нравится оборачивать API синглетами или классами, которые состоят только из статических функций, но зачем вообще беспокоиться об этом классе? Мне кажется, даже проще просто вызывать функции (обернутые, как вы хотите), но затем внутренне получить доступ к «глобальному» состоянию. Например, Log :: GetInstance () -> LogError (...) также может быть LogError (...), который внутренне обращается к любому глобальному состоянию, не требуя, чтобы код клиента знал об этом.
тир-том- взрывы
0

Только когда у вас есть элемент, который имеет только один контроллер, но обрабатывается несколькими модулями.

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

Заратустра
источник
-1

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

Синглтоны - лучший глобальный ИМО и, следовательно, правильный инструмент.

Используйте это экономно!

jacmoe
источник
Гораздо быстрее, чем что ? Глобалы будут находиться на некоторой странице случайной памяти, если они являются указателем, и на некоторой фиксированной, но, вероятно, удаленной странице памяти, если они являются значением. Компилятор не может использовать на них полезные оптимизации глазка. По любым параметрам, которые я могу придумать, глобалы медленные .
Быстрее, чем геттеры и сеттеры и раздача ссылок. Но не очень. Это помогает уменьшить размеры, так что это поможет в некоторых системах. Я, вероятно, преувеличил, когда сказал «много», но я очень скептически отношусь к тому, что кто-то говорит, что вы не должны что-то использовать. Вам просто нужно использовать свой здравый смысл. В конце концов, синглтоны - это не более чем статичный член класса, а это глобальные переменные.
Жакмо
Но для решения любых проблем синхронизации с глобальными переменными вам нужны геттеры / сеттеры, так что это не очень актуально. Проблема с (и редким преимуществом) глобального состояния заключается в том, что это глобальное состояние, а не какие-либо проблемы со скоростью или (эффективно) синтаксическими аспектами интерфейса.