¿Hay casos en que los globals / singletons son útiles en el desarrollo de juegos? [cerrado]

11

Sé que tener variables globales o clases de singleton crea casos que pueden ser difíciles de probar / administrar y me he visto obligado a usar esos patrones en el código, pero a menudo tienes que enviarlos.

Entonces, ¿hay casos en que las variables globales o singletons sean realmente útiles en el desarrollo de juegos?

Ólafur Waage
fuente

Respuestas:

30

Estas cosas siempre pueden ser útiles . Si es o no la solución más bonita o más segura es otra cuestión, pero creo que el desarrollo del juego implica un cierto grado de pragmatismo.

JasonD
fuente
Muy cerca de mi mantra.
Ólafur Waage
15

Definitivamente una lista no exhaustiva, pero aquí va:

Contras

  • Gestión de por vida . Los Singletons y Globals pueden comenzar antes de que se inicialicen los sistemas clave (por ejemplo, el montón), dependiendo de cómo los configure. Si alguna vez quieres derribarlos (útil si estás haciendo un seguimiento de fugas, por ejemplo), debes tener cuidado con el orden de desmontaje o comenzar a meterte en cosas como los Phoenix Singletons . (Ver fiasco de orden de inicialización estática ).
  • Control de acceso . ¿El renderizado solo debe tener acceso constante a los datos del juego, mientras que la actualización no es constante? Esto puede ser más difícil de hacer cumplir con singletons y globals.
  • Estado . Como señaló Kaj, es más difícil descomponer funcionalmente y transformar singletons para que funcionen en una arquitectura de memoria no compartida. También tiene implicaciones potenciales de rendimiento para otros tipos de sistemas NUMA (acceso a memoria que no es local). Los singletons generalmente representan un estado centralizado y, por lo tanto, son la antítesis de las transformaciones que la pureza facilita.

Ya sea a favor o en contra

  • Concurrencia . En un entorno concurrente, los singletons pueden ser una molestia (debe considerar los problemas de la carrera de datos / reentrada) o una bendición (es más fácil centralizar y razonar sobre el bloqueo y la gestión de recursos). El uso inteligente de cosas como el almacenamiento local de subprocesos puede mitigar algunos problemas potenciales, pero generalmente no es un problema fácil.
  • Codegen (dependiendo del compilador, la arquitectura, etc.): para una implementación ingenua de creación única en el primer uso, puede estar evaluando una rama condicional adicional para cada acceso. (Esto puede sumar). Múltiples globales utilizados en una función pueden inflar el grupo literal . Un enfoque de "estructura de todos los globales" puede ahorrar espacio en el grupo literal: solo una entrada a la base de la estructura, y luego compensaciones codificadas en las instrucciones de carga. Finalmente, si está evitando los globales y los singletons a toda costa, generalmente necesita usar al menos un poco de memoria adicional (registros, pila o montón) para pasar punteros, referencias o incluso copias.

Pros

  • Simplicidad . Si sabe que los inconvenientes enumerados anteriormente no le afectan tanto (por ejemplo, está trabajando en un entorno de subproceso único para una plataforma portátil de CPU única), evite cierta arquitectura (como el paso de argumentos mencionado anteriormente). Los singletons y globals pueden ser más fáciles de entender para los codificadores menos experimentados (aunque puede ser más fácil usarlos incorrectamente).
  • Gestión de por vida (nuevamente). Si usa una "estructura de globales" o algún otro mecanismo que no sea crear por primera vez, puede tener un control fácil de leer y detallado sobre el orden de inicialización y destrucción. Automatiza esto hasta cierto punto, o lo administra manualmente (tener que administrarlo puede ser un profesional o un estafador, dependiendo del número de globales / singletons y sus interdependencias).

Usamos mucho una "estructura de singletons globales" en nuestros títulos portátiles. Los títulos de PC y consola tienden a depender menos de ellos; cambiaremos más hacia una arquitectura de mensajería / dirigida por eventos. Dicho esto, los títulos de PC / consola todavía usan a menudo un TextureManager central; ya que generalmente envuelve un único recurso compartido (la memoria de textura), esto tiene sentido para nosotros.

Si mantiene su API relativamente limpia, puede que no sea demasiado difícil refactorizar (¡o usar!) Un patrón singleton cuando lo necesite ...

leander
fuente
Gran lista Personalmente, solo encuentro un valor negativo en singletons y globals debido a los problemas que provienen del estado compartido (probablemente mutable). Sin embargo, no creo que el problema del "codegen" sea un problema; o necesita pasar los punteros a través de los registros o necesita hacer una secuencia de operaciones para obtenerlo, creo que cambia el código frente al tamaño de los datos a la vista, pero la suma será casi la misma.
dash-tom-bang
Sí, con respecto al codegen, lo único que hemos visto realmente hacer una diferencia significativa fue pasar de globales globales a una tabla global: redujo los grupos literales y, por lo tanto, el tamaño de la sección .text, en varios por ciento. Lo cual es algo muy bueno en sistemas pequeños. =) Los beneficios de rendimiento de no golpear tanto el dcache para los literales fue solo un beneficio secundario agradable (aunque pequeño en la mayoría de los casos). Otra ventaja de pasar a una tabla global era la capacidad de moverlo fácilmente a una sección que residía en una memoria más rápida ...
leander
4

Pueden ser muy útiles, especialmente durante la creación de prototipos o la implementación experimental, pero en general preferimos pasar referencias para estructuras similares a las de los gerentes, de esa manera al menos tiene cierto control sobre desde dónde se accede. El mayor problema con los globales y los singletons (en mi opinión) es que no son muy compatibles con múltiples subprocesos, y el código que los usa es mucho más difícil de transportar a la memoria no compartida, como las SPU.

Kaj
fuente
2

Yo diría que el diseño de singleton en sí mismo no es útil en absoluto. Definitivamente, las variables globales pueden ser útiles, pero prefiero verlas ocultas detrás de una interfaz bien escrita para que no sepa de su existencia. Con los singletons eres definitivamente consciente de su existencia.

A menudo uso variables globales para cosas que necesitan acceso a través de un motor. Mi herramienta de rendimiento es un buen ejemplo que utilizo en todo el motor para llamar. Las llamadas son simples; ProbeRegister (), ProbeHit () y ProbeScoped (). Su acceso real es un poco más complicado y utiliza variables globales para algunas de sus cosas.

Simón
fuente
2

El principal problema con los globales y los singletons mal implementados son los oscuros errores de construcción y deconstrucción.

Entonces, si trabaja con primitivas que no tienen estos problemas o son muy conscientes del problema con un puntero. Entonces se pueden usar de forma segura.

Los globales tienen su lugar, igual que los gotos y no deben descartarse sin más, sino que deben usarse con cuidado.

Hay una buena explicación en la Guía de estilo de Google C ++

Kimau
fuente
No estoy de acuerdo con que ese sea el problema principal; "No usar globales" se remonta más allá de los constructores de existencia. Yo diría que hay dos problemas principales con ellos. Primero, complican el razonamiento sobre un programa. Si tengo una función que accede a un global, el comportamiento de esa función depende no solo de sus propios argumentos, sino también de todas las demás funciones que acceden al global. Eso es mucho más para pensar. En segundo lugar, incluso si puede mantenerlo todo en su cabeza (no puede hacerlo), todavía resultan en problemas de concurrencia más complicados.
@ Joe, la palabra clave es "dependencias". Una función que accede a los globales (o singletons o cualquier otro estado compartido) tiene dependencias implícitas de todas esas cosas. Es mucho más fácil razonar sobre un poco de código si todas sus dependencias son explícitas, que es lo que se obtiene cuando la lista completa de dependencias se documenta en la lista de argumentos.
dash-tom-bang
1

Los globales son útiles al crear rápidamente prototipos de un sistema que requiere cierto estado entre las llamadas a funciones. Una vez que haya establecido que el sistema funciona, mueva el estado a una clase y convierta las funciones en métodos de esa clase.

Los Singletons son útiles para causar problemas a usted mismo y a los demás. Mientras más estado global introduzca, más problemas tendrá con la corrección del código, mantenimiento, extensibilidad, concurrencia, etc. Simplemente no lo haga.

Kylotan
fuente
1

Tiendo a recomendar el uso de algún tipo de contenedor DI / IoC con administración personalizada de por vida en lugar de singletons (incluso si usa un administrador de por vida de "instancia única"). Al menos, es fácil cambiar la implementación para facilitar las pruebas.

Mike Strobel
fuente
Para aquellos de ustedes que no lo saben, DI es "inyección de dependencia" e IoC es "inversión de control". Enlace: martinfowler.com/articles/injection.html (Había leído sobre esto antes pero nunca había visto el acrónimo, por lo que me tomó un poco de búsqueda).
leander
0

Los Singleton son una excelente manera de almacenar un estado compartido en un prototipo temprano.

No son una bala de plata y vienen con algunos problemas, pero son un patrón muy útil para ciertos estados de IU / Lógica.

Por ejemplo, en iOS, usas singletons para obtener la [UIApplication sharedApplication], en cocos2d puedes usarla para obtener referencias a ciertos objetos como [CCNotifications sharedManager] y, personalmente, generalmente comienzo con un singleton [Game sharedGame] donde puedo estado de tienda que se comparte entre muchos componentes diferentes.

DFectuoso
fuente
0

Wow, esto es interesante para mí, ya que nunca he tenido un problema personal con el patrón singleton. Mi proyecto actual es un motor de juego C ++ para Nintendo DS, y estoy implementando muchas de las utilidades de acceso de hardware (es decir, VRAM Banks, Wifi, los dos motores gráficos) como instancias únicas, porque están destinadas a envolver C global funciones en la biblioteca subyacente.


fuente
Mi pregunta sería entonces, ¿por qué usar singletons? Veo que a la gente le gusta envolver las API con singletons o clases que consisten solo en funciones estáticas, pero ¿por qué molestarse con la clase? Me parece aún más fácil tener las llamadas de función (ajustadas como lo desee), pero luego acceder internamente al estado "global". Por ejemplo, Log :: GetInstance () -> LogError (...) podría ser LogError (...) que accede internamente a cualquier estado global sin requerir que el código del cliente lo sepa.
dash-tom-bang
0

Solo cuando tiene un elemento que solo tiene un controlador pero es manejado por varios módulos.

Como, por ejemplo, la interfaz del mouse. O interfaz de joystick. O reproductor de música. O reproductor de sonido. O la pantalla. O el administrador de guardar archivos.

zaratustra
fuente
-1

¡Los globales son mucho más rápidos! Por lo tanto, encajaría perfectamente para una aplicación intensiva en rendimiento, como un juego.

Los Singletons son una mejor herramienta global, IMO y, por lo tanto, la herramienta correcta.

¡Úselo con moderación!

jacmoe
fuente
¿Mucho más rápido que qué ? Los globales estarán en alguna página de memoria aleatoria si son un puntero, y en alguna página de memoria fija pero probablemente distante si son un valor. El compilador no puede usar ninguna optimización de mirilla útil en ellos. En cualquier medida que se me ocurra, los globales son lentos .
Más rápido que getters y setters y pasando referencias. Pero no por mucho. Ayuda a mantener los tamaños bajos, por lo que ayudaría en algunos sistemas. Probablemente exageré cuando dije 'mucho', pero soy muy escéptico de que alguien diga que no debes usar algo. Solo necesitas usar tu sentido común. Después de todo, los singletons no son más que un miembro de clase estático, y esos son globales.
jacmoe
Pero para resolver cualquier problema de sincronización con los globales, necesita getters / setters para ellos, por lo que eso no es realmente relevante. El problema con el (y raro beneficio) del estado global es que es un estado global, no tiene ninguna preocupación por la velocidad o los aspectos (efectivamente) de sintaxis de la interfaz.