¿Por qué es tan malo optimizar demasiado temprano?

168

Después de analizar un poco la optimización, descubrí (literalmente en todas partes) que parece ser un pecado universalmente reconocido optimizar un juego demasiado pronto.

Realmente no entiendo esto, ¿no sería increíblemente difícil cambiar algunas de las estructuras centrales del juego al final, en lugar de desarrollarlas la primera vez teniendo en cuenta el rendimiento?

Entiendo que esperar hasta que el juego termine te dirá si incluso necesitas optimizaciones, pero si no lo haces de todos modos, después de todo, podría ampliar la variedad de dispositivos en los que el juego podría ejecutarse, lo que aumentaría la cantidad de potencial jugadores

¿Podría alguien explicarme por qué es una mala idea optimizar demasiado pronto?

señor-mate
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Josh
3
No podría ser más fácil responder esto: "se pierde el tiempo". También podría preguntar "¿por qué tratar de ahorrar dinero al comprar?"
Fattie
27
Por el contrario, tenga en cuenta que muchos ingenieros simplemente dicen que la frase "no optimizar demasiado pronto" es simplemente incorrecta . La oración debería ser, simplemente, "no optimices hasta que sepas qué optimizar". Entonces, simplemente, si hay tres sistemas A, B y C, es completamente inútil optimizar A si resulta que A no es de ninguna manera un cuello de botella, y C es de hecho el cuello de botella. En ese ejemplo, obviamente, optimizar A habría sido una pérdida de tiempo total. Entonces, simplemente, no optimices hasta que sepas cuál de A, BC optimizar: eso es todo lo que significa, es así de simple.
Fattie
2
La optimización de la clase de tiempo de su aplicación durante el desarrollo es muy diferente a tratar de eliminar algunas instrucciones de un ciclo que generalmente es tiempo constante. Concéntrese en O (n) vs O (n log n) en lugar de "escribir un bucle for de esta manera guarda una instrucción de CPU".
1
@phyrfox En realidad, reducir el tiempo de carga me parece más impresionante. El tiempo de carga de tres segundos es notable. No estoy seguro si 55fps a 60fps es notable.
Immibis

Respuestas:

237

Preámbulo:

Se han planteado algunas objeciones en los comentarios, y creo que en gran parte se derivan de un malentendido de lo que queremos decir cuando decimos "optimización prematura", por lo que quería agregar una pequeña aclaración sobre eso.

"No optimizar de forma prematura" no significa "escribir código que sabes que es malo, porque Knuth dice que no puedes limpiarlo hasta el final"

Significa "no sacrifique el tiempo y la legibilidad para la optimización hasta que sepa qué partes de su programa realmente necesitan ayuda para ser más rápido". Dado que un programa típico pasa la mayor parte de su tiempo en algunos cuellos de botella, invertir en optimizar "todo" podría no obtener el mismo aumento de velocidad que centrar esa misma inversión en solo el código de cuello de botella.

Esto significa que, en caso de duda , debemos:

  • Prefiere código simple de escribir, claro de entender y fácil de modificar para empezar

  • Verifique si se necesita una mayor optimización (generalmente perfilando el programa en ejecución, aunque un comentario a continuación señala que hace un análisis matemático: el único riesgo es que también debe verificar que sus matemáticas sean correctas)

Una optimización prematura no es :

  • Decisiones arquitectónicas para estructurar su código de una manera que se adapte a sus necesidades, eligiendo los módulos / responsabilidades / interfaces / sistemas de comunicación apropiados de una manera considerada.

  • Eficiencias simples que no requieren tiempo extra o hacen que su código sea más difícil de leer. Cosas como el uso de mecanografía fuerte puede ser eficiente y hacer que su intención sea más clara. El almacenamiento en caché de una referencia en lugar de buscarla repetidamente es otro ejemplo (siempre que su caso no exija una lógica de invalidación de caché compleja, tal vez espere a escribir eso hasta que haya perfilado la forma simple primero).

  • Usando el algoritmo correcto para el trabajo. A * es más óptimo y más complejo que buscar exhaustivamente un gráfico de trazado de ruta. También es un estándar de la industria. Repetir el tema, apegarse a métodos probados y verdaderos como este puede hacer que su código sea más fácil de entender que si hace algo simple pero contrario a las mejores prácticas conocidas. Si tiene experiencia en cuellos de botella al implementar la función X del juego de una manera en un proyecto anterior, no necesita volver a tocar el mismo cuello de botella en este proyecto para saber que es real: puede y debe reutilizar soluciones que han funcionado en el pasado. juegos.

Todos esos tipos de optimizaciones están bien justificados y, por lo general, no se etiquetarían como "prematuros" (a menos que se esté yendo por un agujero de conejo que implementa la búsqueda de caminos de vanguardia para su mapa de tablero de ajedrez 8x8 ...)

Entonces, con eso aclarado, sobre por qué podríamos encontrar esta política útil en los juegos específicamente:


Especialmente en gamedev, la velocidad de iteración es lo más preciado. A menudo implementaremos y volveremos a implementar muchas más ideas de las que finalmente se enviarán con el juego terminado, tratando de "encontrar la diversión".

Si puede crear un prototipo de un mecánico de una manera sencilla y tal vez un poco ingenua y probarlo al día siguiente, está en una posición mucho mejor que si pasara una semana haciendo primero la versión más óptima. Especialmente si resulta que apesta y terminas descartando esa característica. Hacerlo de manera simple para que pueda realizar la prueba antes puede ahorrar una tonelada de trabajo desperdiciado optimizando el código que no conserva.

El código no optimizado también es generalmente más fácil de modificar y probar diferentes variantes que el código que está afinado para hacer una cosa precisa de manera óptima, que tiende a ser frágil y difícil de modificar sin romperlo, introducir errores o ralentizarlo. Por lo tanto, mantener el código simple y fácil de cambiar a menudo vale un poco de ineficiencia en el tiempo de ejecución durante la mayor parte del desarrollo (generalmente estamos desarrollando en máquinas por encima de la especificación objetivo, para que podamos absorber los gastos generales y centrarnos en obtener la experiencia objetivo primero) hasta que podamos Hemos bloqueado lo que necesitamos de la función y podemos optimizar las piezas que ahora sabemos que son lentas.

Sí, refactorizar partes del proyecto tarde en el desarrollo para optimizar los puntos lentos puede ser difícil. Pero también se está refactorizando repetidamente a lo largo del desarrollo porque las optimizaciones que realizó el mes pasado no son compatibles con la dirección en la que ha evolucionado el juego desde entonces, o estaban arreglando algo que resultó no ser el verdadero cuello de botella una vez que obtuvo más características y contenido en.

Los juegos son extraños y experimentales: es difícil predecir cómo evolucionará un proyecto de juego y sus necesidades tecnológicas y dónde el rendimiento será más ajustado. En la práctica, a menudo terminamos preocupándonos por las cosas equivocadas: busque entre las preguntas de rendimiento aquí y verá surgir un tema común de desarrolladores que se distraen con cosas en papel que probablemente no sean un problema en absoluto.

Para tomar un ejemplo dramático: si su juego está vinculado a la GPU (no es raro), todo ese tiempo dedicado a la hiperoptimización y al enhebrado del trabajo de la CPU podría no producir ningún beneficio tangible. En su lugar, podrían haberse invertido todas esas horas de desarrollo implementando y puliendo las características del juego, para una mejor experiencia del jugador.

En general, la mayor parte del tiempo que pasas trabajando en un juego no se gastará en el código que termina siendo el cuello de botella. Especialmente cuando trabajas en un motor existente, las cosas súper caras del bucle interno en los sistemas de representación y física están en gran parte fuera de tu alcance. En ese momento, su trabajo en los guiones de juego es básicamente mantenerse fuera del camino del motor, siempre que no arroje una llave inglesa, entonces probablemente saldrá bastante bien para una primera compilación.

Por lo tanto, aparte de un poco de higiene de código y presupuesto (por ejemplo, no busque / construya repetidamente cosas si puede reutilizarlo fácilmente, mantenga modestas sus consultas de búsqueda de caminos / física o lecturas de GPU modestas, etc.), con el hábito de no terminar -optimizar antes de saber dónde están los problemas reales resulta ser bueno para la productividad, ahorrándonos la pérdida de tiempo optimizando las cosas incorrectas y manteniendo nuestro código más simple y fácil de ajustar en general.

DMGregory
fuente
38
Aquí hay más discusión sobre StackOverflow , enfatizando la importancia de la legibilidad y la claridad en el código, y el peligro de hacer que el código sea más difícil de entender (y más fácil introducir errores) al optimizarlo prematuramente.
DMGregory
8
Gran respuesta, me gustaría agregar, en respuesta a " ¿no sería increíblemente difícil cambiar algunas de las estructuras centrales del juego al final, en lugar de desarrollarlas la primera vez con el rendimiento en mente? " por supuesto, intenta diseñar para la eficiencia en primer lugar. Cuando se le presentan dos opciones, elige la más eficiente. De lo contrario, escribes el juego de la forma más modular posible con interfaces bien definidas. Luego puede cambiar los mecanismos dentro de cada módulo sin reestructurar toda la base de código.
Baldrickk
1
@Baldrickk Diría que vale la pena compartirlo como respuesta adicional. (¡Lo votaría!) Mi respuesta omite la importancia de la arquitectura y la planificación para evitar crear grandes problemas en primer lugar. En cuanto a la referencia de Gizmo al "desarrollo de programas en general", de ahí proviene este concepto de optimización prematura. Como se describió anteriormente, la optimización incorrecta puede convertirse en una deuda técnica tan fácilmente como una optimización faltante. Pero debemos enfatizar que nunca estamos haciendo las cosas de una manera deliberadamente lenta, solo preferimos la simplicidad y la legibilidad hasta que podamos perfilar y encontrar los problemas reales
DMGregory
1
Aunque probablemente soy el más inexperto de ustedes, debo decir que encuentro esta "raíz de optimización prematura de todo mal" un poco rara. Hace dos años más o menos, estaba tratando de hacer algo en Unity3D. Escribí algunas consultas LINQ, cada una utilizando la anterior. Miré mi código y comencé a tener fuertes dudas. Pensé que la complejidad del cálculo era horrible. Tomé una hoja de papel y calculé que procesar estas consultas en un tamaño de datos esperado tomaría horas. Así que agregué un poco de caché aquí y allá. Y las consultas iban bien.
gaazkam
3
Así que hizo la diligencia debida y verificó que el problema era real antes de cambiar su código de lo que inicialmente era más claro e intuitivo para usted. Eso significa que su optimización no fue prematura. ;) "Evitar sobreoptimizar prematuramente" no significa "escribir código innecesariamente costoso", solo para favorecer la simplicidad, la legibilidad y la facilidad de modificación hasta que se haya identificado un problema de rendimiento definitivo. Los comentarios no están destinados a discusión, por lo que podemos pasar al chat si desea hablar más sobre esto.
DMGregory
42

nota: esta respuesta comenzó como un comentario sobre la respuesta de DMGregory, por lo que no duplica los muy buenos puntos que hace.

"¿No sería increíblemente difícil cambiar algunas de las estructuras centrales del juego al final, en lugar de desarrollarlas la primera vez teniendo en cuenta el rendimiento?"

Esto, para mí, es el quid de la cuestión.

Al crear su diseño original, debe intentar diseñar para obtener eficiencia, al más alto nivel. Esto es menos optimización y se trata más de la estructura.

Ejemplo:
necesita crear un sistema para cruzar un río. Los diseños obvios son un puente o un ferry, entonces, ¿cuál eliges?
La respuesta, por supuesto, depende del tamaño del cruce y del volumen del tráfico. Esto no es una optimización, sino comenzar con un diseño adecuado para su problema.

Cuando se le presentan opciones de diseño, elige la que mejor se adapte a lo que desea hacer.

Entonces, digamos que nuestro volumen de tráfico es bastante bajo, por lo que decidimos construir dos terminales y comprar en un ferry para manejar el tráfico. Una implementación sencilla y agradable
Lamentablemente, una vez que la tenemos en funcionamiento, descubrimos que está viendo más tráfico del esperado. ¡Necesitamos optimizar el ferry! (porque funciona, y construir un puente ahora no es un buen plan)

Opciones:

  • Compre un segundo ferry (procesamiento paralelo)
  • Agregue otra cubierta de automóvil al ferry (compresión del tráfico)
  • Actualice el motor del ferry para hacerlo más rápido (algoritmos de procesamiento reescritos)

Aquí es donde debe intentar hacer que su diseño original sea lo más modular posible.
Todo lo anterior son posibles optimizaciones, e incluso podría hacer las tres.
Pero, ¿cómo hacer estos cambios sin grandes cambios estructurales?

Si tiene un diseño modular con interfaces claramente definidas, entonces debería ser sencillo implementar estos cambios.
Si su código no está estrechamente acoplado, los cambios en los módulos no afectan la estructura circundante.

Echemos un vistazo a agregar un ferry adicional.
Se podría construir un programa 'malo' en torno a la idea de un solo ferry, y tener los estados del muelle y el estado y la posición del ferry agrupados y compartiendo el estado. Esto será difícil de modificar para permitir que se agregue un ferry adicional al sistema.
Un mejor diseño sería tener los muelles y el ferry como entidades separadas. No existe un acoplamiento estrecho entre ellos, pero tienen una interfaz, donde un ferry puede llegar, descargar pasajeros, tomar nuevos y partir. El muelle y el ferry comparten solo esta interfaz, y esto hace que sea fácil realizar cambios en el sistema, en este caso agregando un segundo ferry. El muelle no se preocupa por los transbordadores que hay en realidad, todo lo que le preocupa es que algo (cualquier cosa) esté usando su interfaz.

tl; dr:

  • Trate de diseñar para la eficiencia en primer lugar.
  • Cuando se le presentan dos opciones, elige la más eficiente.
  • Escriba su código de la forma más modular posible con interfaces bien definidas.

Luego puede cambiar los mecanismos dentro de cada módulo sin reestructurar toda la base de código cuando necesite optimizar.

Baldrickk
fuente
16
Al principio, también puede implementar su ferry como IRiverCrossing, y cuando quede claro que el volumen de tráfico es demasiado grande para la configuración del ferry, implemente el puente como IRiverCrossingy suéltelo. El truco, por supuesto, es reducir la API externa del Ferry y el puente a la funcionalidad común para que puedan ser representados por la misma interfaz.
Mr.Mindor
2
Incluso puede tener pruebas automatizadas escritas para estas interfaces modulares, por lo que puede refactorizar rápidamente sin preocuparse por romper nada fuera del módulo que está tocando.
user3067860
2
Muy buen ejemplo de por qué la OOP es tan importante en estos días.
Jaime Gallego
1
Has acertado en la razón correcta. El mantra de Donald Knuth solo es cierto cuando la velocidad no es uno de los requisitos principales, y un enfoque en la optimización comprometería el diseño principal. Por desgracia, en los juegos, la velocidad a menudo está en el centro y, por lo tanto, debe tenerse en cuenta en el diseño desde el principio. La preocupación del OP (y su respuesta) está completamente justificada.
Peter A. Schneider
3
@AlexandreVaillancourt en lugar de responder el título de la pregunta, traté de responder lo que creo que es la parte clave de la pregunta que realmente se está respondiendo; es decir, "¿por qué no debería optimizar temprano si la optimización será mucho más trabajo más tarde porque mi diseño es fijo" (parafraseado) ? Esta respuesta también comenzó como un comentario sobre la respuesta de DMGregory, y es adicional a ella, y no tiene mucho sentido duplicar su respuesta.
Baldrickk
24

"No optimizar temprano" no significa "elegir la peor forma posible de hacer las cosas". Aún debe tener en cuenta las implicaciones de rendimiento (a menos que solo esté creando prototipos). El punto no es paralizar otras cosas más importantes en ese punto del desarrollo, como la flexibilidad, la confiabilidad, etc. Elija optimizaciones simples y seguras: elija las cosas que limita y las que mantiene libres; realizar un seguimiento de los costos. ¿Deberías usar tipeo fuerte? La mayoría de los juegos funcionaban y funcionaban bien; ¿cuánto le costaría eliminar eso si encuentra usos interesantes de la flexibilidad para gamemplay?

Es mucho más difícil modificar el código optimizado, especialmente el código "inteligente". Siempre es una elección que hace que algunas cosas sean mejores y otras peores (por ejemplo, podría estar cambiando el tiempo de CPU por el uso de memoria). Al tomar esa decisión, debe ser consciente de todas las implicaciones: pueden ser desastrosas, pero también pueden ser útiles.

Por ejemplo, Commander Keen, Wolfenstein y Doom fueron construidos sobre un motor de renderizado optimizado. Cada uno tenía su "truco" que permitía que el juego existiera en primer lugar (cada uno también tenía más optimizaciones desarrolladas con el tiempo, pero eso no es importante aquí). Eso está bien . Está bien optimizar en gran medida el núcleo del juego, la idea que lo hace posible; especialmente si estás explorando un nuevo territorio donde esta característica optimizada en particular te permite considerar diseños de juegos que no fueron muy explorados. Las limitaciones que introduce la optimización también pueden brindarle un juego interesante (por ejemplo, los límites de conteo de unidades en los juegos RTS pueden haber comenzado como una forma de mejorar el rendimiento, pero también tienen un efecto de juego).

Pero tenga en cuenta que en cada uno de estos ejemplos, el juego no podría existir sin la optimización. No comenzaron con un motor "completamente optimizado", comenzaron con la simple necesidad y avanzaron. Desarrollaban nuevas tecnologías y las usaban para hacer juegos divertidos. Y los trucos del motor se limitaron a una parte tan pequeña de la base de código como sea posible: las optimizaciones más pesadas solo se introdujeron cuando el juego se realizó en su mayoría, o cuando permitió que surgiera una nueva característica interesante.

Ahora considera un juego que tal vez quieras hacer. ¿Existe realmente algún milagro tecnológico que haga o rompa ese juego? Tal vez estás imaginando un juego de mundo abierto en un mundo infinito. ¿Es realmente la pieza central del juego? ¿El juego simplemente no funcionaría sin él? Tal vez estás pensando en un juego donde el terreno es deformable sin límite, con una geología realista y tal; ¿Puedes hacer que funcione con un alcance más pequeño? ¿Funcionaría en 2D en lugar de 3D? Obtenga algo divertido lo antes posible, incluso si las optimizaciones pueden requerir que modifique una gran parte de su código existente, puede valer la pena; e incluso te darás cuenta de que hacer las cosas más grandes realmente no mejora el juego.

Como ejemplo de un juego reciente con muchas optimizaciones, señalaría Factorio. Una parte crítica del juego son los cinturones: hay muchos miles de ellos y llevan muchos trozos individuales de materiales por toda la fábrica. ¿Comenzó el juego con un motor de correa altamente optimizado? ¡No! De hecho, el diseño original del cinturón era casi imposible de optimizar: hacía una simulación física de los elementos del cinturón, lo que creaba algunas cosas interesantes que podía hacer (esta es la forma en que obtiene un juego "emergente", un juego que sorprende el diseñador), pero significaba que tenía que simular cada elemento del cinturón. Con miles de correas, obtienes decenas de miles de elementos simulados físicamente, incluso solo quitándolos y dejando que las correas hagan el trabajo te permiten reducir el tiempo de CPU asociado en un 95-99%, incluso sin considerar cosas como la localidad de memoria. Pero solo es útil hacerlo cuando realmente alcanzas esos límites.

Casi todo lo que tenía que ver con los cinturones tuvo que rehacerse para permitir que los cinturones se optimizaran. Y los cinturones necesitaban ser optimizados, porque necesitabas muchos cinturones para una gran fábrica, y las grandes fábricas son una atracción del juego. Después de todo, si no puedes tener grandes fábricas, ¿por qué tener un mundo infinito? Es curioso que preguntes: las versiones anteriores no lo hicieron :) El juego fue reelaborado y remodelado muchas veces para llegar a donde están ahora, incluida una nueva versión del 100% cuando se dieron cuenta de que Java no es el camino a seguir para juego como este y se cambió a C ++. Y funcionó muy bien para Factorio (aunque todavía era algo bueno, no estaba optimizado desde el principio, especialmente porque se trataba de un proyecto de pasatiempo, que podría haber fallado simplemente por falta de interés).

Pero la cosa es, no sonPuedes hacer muchas cosas con una fábrica de alcance limitado, y muchos juegos han demostrado eso. Los límites pueden ser aún más motivadores para la diversión que las libertades; ¿Sería más divertido Spacechem si los "mapas" fueran infinitos? Si comenzaste con "cinturones" muy optimizados, te verías obligado a seguir ese camino; y no podría explorar otras direcciones de diseño (como ver qué cosas interesantes puede hacer con cintas transportadoras simuladas por la física). Estás limitando tu potencial espacio de diseño. Puede que no parezca así porque no ves muchos juegos sin terminar, pero la parte difícil es hacer la diversión correcta: por cada juego divertido que ves, probablemente haya cientos que simplemente no pudieron llegar y fueron descartados (o peor, lanzado como desorden horrible). Si la optimización lo ayuda a hacerlo, continúe. Si no es así ... Es probable que sea prematuro. Si crees que una mecánica de juego funciona muy bien, pero necesita optimizaciones para brillar realmente, adelante. Si no tienes una mecánica interesante,No los optimices . Encuentra la diversión primero: encontrarás que la mayoría de las optimizaciones no ayudan con eso, y a menudo son perjudiciales.

Finalmente, tienes un gran juego divertido. ¿Tiene sentido optimizar ahora ? ¡Decir ah! Todavía no está tan claro como podría pensar. Hay algo divertidopuedes hacer en su lugar? No olvides que tu tiempo aún es limitado. Todo requiere un esfuerzo, y desea centrar ese esfuerzo en donde más importa. Sí, incluso si estás haciendo un "juego gratis" o un juego de "código abierto". Mira cómo se juega el juego; observe dónde el rendimiento se convierte en un cuello de botella. ¿La optimización de esos lugares es más divertida (como construir fábricas cada vez más grandes y enredadas)? ¿Le permite atraer a más jugadores (por ejemplo, con computadoras más débiles o en diferentes plataformas)? Siempre debe priorizar: busque la relación esfuerzo / rendimiento. Es probable que encuentres mucha fruta baja simplemente jugando tu juego y viendo a otros jugar el juego. Pero tenga en cuenta la parte importante: para llegar allí, necesita un juego . Concéntrate en eso.

Como guinda, considera que la optimización nunca termina. No es una tarea con una pequeña marca de verificación que finaliza y pasa a otras tareas. Siempre hay "una optimización más" que puede hacer, y una gran parte de cualquier desarrollo es comprender las prioridades. No optimiza por el bien de la optimización, lo hace para lograr un objetivo particular (por ejemplo, "200 unidades en la pantalla a la vez en un Pentium de 333 MHz" es un gran objetivo). No pierda la noción del objetivo terminal solo porque se concentra demasiado en los objetivos intermedios que tal vez ya no sean requisitos previos para el objetivo terminal.

Luaan
fuente
Creo que los desarrolladores de factorio ahora están optimizando nuevamente los cinturones para que solo necesiten simular elementos periódicamente, y en cualquier otro momento solo interpola su posición cuando los miras . Pero solo lo están haciendo ahora cuando todo el juego se ha basado efectivamente en cinturones (y robots) durante mucho tiempo y no hasta que las personas comenzaron a notar y quejarse del rendimiento.
Immibis
2
@immibis Exactamente. Hacer que funcione mejor antes de que la gente realmente alcance los límites sería un desperdicio de recursos, que se gastan mejor en otros lugares. Incluso con las últimas optimizaciones, lo más probable es que encuentres el problema solo para las megafactorías más allá del alcance del juego normal (lanzamiento del cohete). Pero habilitó la nueva configuración de juego de Marathon, que requiere una logística mucho más pesada. Y nuevamente, identificaron los cuellos de botella al obtener estadísticas de las personas que realmente jugaban el juego: aproximadamente la mitad de los cuellos de botella fueron una gran sorpresa para ellos.
Luaan
@Fattie ¿Entiendes la pregunta, pero no la respuesta? ¿Estás tratando de decir que es posible una respuesta más simple (por ejemplo, "Economía")? No veo cómo se supone que tu comentario sea constructivo.
Luaan
2
@Fattie Si cree que la respuesta es "Optimizar solo una vez que sepa cuál es el cuello de botella", publíquelo como respuesta. Ya has gastado muchas más palabras de las necesarias para una buena respuesta, así que adelante. ¿Qué tipo de optimización no mejora y empeora las cosas? Ciertamente nunca he visto uno. Lo fundamental que sigo reiterando es que siempre es una compensación: tiempo que podría gastarse mejor en otro lugar, complejidad que limita su flexibilidad ... Los ejemplos señalan que "temprano" no es un término absoluto; Algunas optimizaciones son necesarias desde el primer día, mientras que otras son perjudiciales.
Luaan
1
@Fattie "Optimizar solo una vez que sepa cuál es el cuello de botella" es la respuesta a "¿Cuándo debo optimizar?" y no es una respuesta a "¿Por qué es tan malo optimizar demasiado pronto?".
immibis
10

Muchas de las respuestas parecen centrarse mucho en el aspecto del rendimiento de la "optimización", mientras que a mí mismo me gusta mirar la optimización y toda la experiencia de optimizar demasiado pronto a un nivel más abstracto.

Compláceme mientras intento elaborar mi perspectiva con la ayuda de los poliominós.

Supongamos que tenemos algunos límites fijos establecidos por el marco o motor con el que estamos trabajando.

ingrese la descripción de la imagen aquí

Luego procedemos a crear nuestra primera capa / módulo del juego así.

ingrese la descripción de la imagen aquí

Continuando construimos nuestra segunda capa / módulo.

ingrese la descripción de la imagen aquí

En este punto, podríamos notar que hay algo de espacio disponible entre los dos módulos y podríamos sentir la tentación de optimizarlo para aprovechar al máximo los límites asignados a nosotros.

ingrese la descripción de la imagen aquí

Perfecto, ahora la aplicación está utilizando todos los recursos disponibles, la aplicación es mejor, ¿verdad?

Procedemos a construir la tercera capa / módulo de nuestra aplicación y de repente nos damos cuenta (tal vez incluso una que no podríamos haber previsto durante la planificación inicial) de que la tercera capa / módulo no funciona para nosotros.

Buscamos una alternativa, encontramos una, y al final, esto también requiere que cambiemos el segundo módulo de nuestra aplicación, ya que es incompatible con nuestro tercer módulo recién seleccionado. (Afortunadamente, es algo compatible con nuestro primer módulo, por lo que no tenemos que volver a escribir todo desde cero).

Así que lo juntamos todo ...

ingrese la descripción de la imagen aquí

Hmm, ¿puedes ver lo que pasó?

Al optimizar demasiado pronto, ahora hemos empeorado las cosas en términos de eficiencia, ya que lo que optimizamos no es lo que terminamos en el futuro.

Y si hubiéramos querido agregar algunos módulos adicionales o cositas adicionales después, es posible que ya no tengamos la capacidad de hacerlo en este momento.

ingrese la descripción de la imagen aquí

Y ajustar el nivel más bajo de nuestro sistema ya no es tan factible ya que se ha enterrado debajo de todas las otras capas.

Sin embargo, si hubiéramos optado por esperar con nuestro impulso de optimizarlo de inmediato, habríamos terminado con algo como esto:

ingrese la descripción de la imagen aquí

Y ahora, si realizamos la optimización en este punto, obtenemos algo satisfactorio a la vista.

ingrese la descripción de la imagen aquí

Esperemos que al menos algunos de ustedes se hayan divertido tanto leyendo esto como yo me haya divertido haciendo esto :) y si ahora sienten que tienen una mejor comprensión del tema, mucho mejor.

Gecko de techo
fuente
3
Estoy eliminando el anuncio. No nos importa cómo hiciste las imágenes; La única parte importante es la suposición de que tiene derecho a publicarlos.
Gnemlock
2
@Gnemlock es justo, aunque mi intención no estaba dirigida a la publicidad, también puedo ver cómo podría malinterpretarse fácilmente como tal, y estoy de acuerdo en que no agrega nada a la respuesta, por lo que no tiene cabida en ella.
Gecko de techo
2
Creo que esta metáfora visual es extremadamente efectiva. Gran enfoque para responder la pregunta! :)
DMGregory
8

Dinero.

Todo se reduce a eso. Dinero. Como el tiempo es dinero *, cuanto más tiempo pase en actividades que no se garantiza que generen más dinero (es decir, no puede considerar estas actividades como inversiones ), más dinero corre el riesgo de desperdiciar y menos dinero ganará con el juego.

Estos son algunos de los posibles efectos secundarios de la optimización demasiado pronto y las razones por las que debe evitarlo:

  • Tus colegas te odian porque juegas en tu caja de arena con tus juguetes mientras trabajan duro para obtener funciones.
  • Los retrasos desagradan a la alta gerencia y hacen que el equipo pierda las fechas de entrega, lo que hace que el juego se lance en la primavera en lugar de antes de la temporada de vacaciones, lo que a su vez hace que el juego no se venda lo suficiente. Y debido a esto, la oficina central decide cerrar el estudio, dejándolo efectivamente sin trabajo. Y sin trabajo significa sin dinero. (Y como a nadie le gustas porque pasaste demasiado tiempo haciendo una optimización temprana, nadie quiere escribir una crítica positiva sobre tu trabajo en LinkedIn, lo que te mantendrá sin dinero por más tiempo).

* Otras respuestas han resaltado bastante bien la parte del "tiempo"


Como nota al margen, en general , la experiencia ayuda a determinar qué es y qué no es una optimización prematura y qué tiene valor para el negocio y qué no.

Por ejemplo, si has trabajado en el juego A y te has dado cuenta al final del proyecto de que la característica XYZ era particularmente pesada en tu ciclo de juego, y eventualmente comienzas a trabajar en el juego B que tiene exactamente la misma característica, decidiendo reescribir la función y optimizarla desde el principio no es una optimización realmente prematura, ya que sabe que será un cuello de botella si no se hace nada.

Alexandre Vaillancourt
fuente
1
@JBentley que es un punto válido. Sin embargo, hay una respuesta consecuente al "por qué" como una implicación directa de la experiencia reunida. Por lo tanto, 1) la optimización temprana puede conducir a la pérdida de tiempo en partes del código que no afectan el tiempo de ejecución promedio (dada la experiencia, debe saber qué construcciones de código son candidatos para las mejores prácticas de optimización) 2) la optimización temprana puede hacer que el código sea más difícil mantener debido a las restricciones de diseño impuestas sobre algún sistema. Por lo tanto, puede ganar muy poco a cambio de ciclos de desarrollo más largos / código engorroso para mantener.
Teodron
3
@JBentley Considere esto como una adición a la respuesta de DMGregory.
Alexandre Vaillancourt
1
Esta parece ser la única respuesta que vale la pena aquí. La pregunta es una tautología. También podría preguntar "Entonces, ¿por qué, en realidad, desea que un juego gane más en lugar de menos dinero en las tiendas de aplicaciones?" ¿Cómo puedes responder eso?
Fattie
@Fattie Es bueno tener este tipo de perspectiva en una respuesta. Pero decir que es la única respuesta que vale la pena es reducir el alcance de la pregunta sin razón. Supone, por ejemplo, que los únicos juegos realizados son dentro de organizaciones con fines de lucro (claramente no es cierto). También está asumiendo que para el OP es obvio que la optimización temprana lleva a más tiempo invertido (y, por lo tanto, más dinero si hablamos de un negocio). En realidad, la pregunta del OP muestra que no está seguro de esto.
JBentley
6

La optimización es, por definición, el proceso de aumentar la eficiencia de una solución hasta el punto en que pierde su eficacia. Este proceso implica entonces una reducción del espacio de la solución.

En una etapa temprana del desarrollo del software todavía puede haber "requisitos ocultos": si reduce demasiado el espacio de su solución, podría terminar en una situación en la que no puede cumplir un "requisito oculto" cuando aparece en una etapa posterior del desarrollo, lo que lo obliga a modificar la arquitectura agregando de esta manera inestabilidad y un posible conjunto de comportamientos no deseados.

La idea es hacer que funcione toda la solución y solo entonces, cuando todos los requisitos estén arreglados e implementados, ajuste el código. Verá entonces que muchas optimizaciones que habría implementado de manera alegre a la vez mientras codifica, ya no son factibles debido a la interacción con los requisitos tardíos.

El espacio real de la solución siempre es mayor que el que esperamos al principio porque no podemos tener un conocimiento perfecto de un sistema muy complejo.

Haz que funcione primero. Luego apriete las cuerdas.

Earendel
fuente
2
Creo que "eficacia" no es la palabra que busca, significa "el poder de producir un efecto", por lo que, en cierto sentido, la optimización se trata de aumentar la eficacia. ¿Vas por alguna versión específica de eficacia? (¿Podrías vincularlo?) ¿Quieres decir algo como flexibilidad?
doppelgreener
2
@doppelgreener Significa que hace que la solución sea más eficiente hasta que deje de producir los efectos que desea ...
immibis
1
"Haz que funcione primero. Luego aprieta las cuerdas". - Eso tiene que valer tanto como el resto de la respuesta combinada. Y por no decir que las otras cosas no estaban bien.
ebyrob
1
@ebyrob: hay otro dicho: "Haz que funcione, hazlo bien
hazlo
@ninjalj Esa también es buena. Por supuesto, mi jefe siempre me dice: "No te apresures, hazlo bien". Así que no sé si lo que está diciendo es tan universal como el director sobre el que el póster original solicita más detalles.
ebyrob
2

En resumen, la optimización temprana se convierte en un esfuerzo desperdiciado si desea cambiar las cosas más tarde, y luego resulta que ha optimizado las estructuras de código fácilmente cambiables en un nivel mucho más bajo, y ahora necesita volver a cambiarlo a un nivel alto enfoque de nivel y optimizar el resultado una vez más.

Este es un error común para los desarrolladores novatos a quienes les gusta enfocarse en obtener placer de "haber hecho un trabajo útil", como la optimización, no pensar si es el momento adecuado para hacerlo. A medida que adquiera experiencia en la programación de grandes proyectos como juegos, aprenderá cuándo se justifica optimizar la base de código existente y cuándo es demasiado pronto. No tenga miedo de cometer este error, solo se beneficiará al aprender de él.

En general, solo optimice si realmente no puede trabajar con la construcción de desarrollo en este momento. Como si estás creando y eliminando millones de objetos 60 veces por segundo.

Por lo tanto, diría que es bueno, en términos de experiencia de aprendizaje, optimizar temprano algunas veces: p

usuario1306322
fuente
2

La optimización se centra en hacer que la computadora funcione mejor en el código, mientras que el desarrollo requiere que el programador funcione mejor en el código.

La optimización no aporta ideas. Hace que la computadora funcione menos y el programador más.

Por supuesto, hay diferentes categorías, como diseñar un automóvil. No hay nada de malo en optimizar un motor antes de construir su primer chasis, pero optimizar el chasis antes de que no sepa la forma del motor puede terminar siendo una pérdida de tiempo. La modularidad y las especificaciones pueden obtener varios lugares viables para el trabajo de optimización incluso antes de que se ensamble el producto en su conjunto.

usuario101307
fuente
Esto mejoraría liderando con algo más que definir la optimización y hablando de la situación práctica en el desarrollo del juego en lugar de buscar analogías sobre los autos.
doppelgreener
Una respuesta perfecta a una pregunta tautológica.
Fattie
2

Estoy totalmente en desacuerdo con todas esas declaraciones de que el código no debe optimizarse en las primeras etapas. Depende de en qué etapa te encuentres. Si este es el prototipo MVP y solo estás tratando de ver el juego que lleva de 1 a 2 semanas, estás listo para reescribir todo lo que ya escribiste. Sí, la optimización no importa. Pero si ya está trabajando en un juego que sabe que se lanzará, debería tener un código optimizado todo el tiempo. Los cuentos que la gente dice sobre nuevas características y cosas que podrían agregarse son conceptos erróneos. Es una arquitectura de código mal diseñada en lugar de un código optimizado. No necesita reescribir nada para agregar nuevos elementos si tiene un buen diseño de código.

Por ejemplo, si tiene un algoritmo de búsqueda de ruta A *, ¿no sería mejor escribirlo de la manera más óptima posible? ¿En lugar de cambiar más tarde la mitad del código que tiene porque tuvo que hacer algunos cambios en el algoritmo porque ahora necesita otro método de llamadas y devoluciones de llamada? Y si ya tiene un código optimizado desde el principio, le ahorrará mucho tiempo. Debido a que puede establecer relaciones entre los objetos y cómo interactúan, en lugar de crear a ciegas toneladas de nuevos scripts y hacer todas las conexiones de inmediato, esto conduce a un código sphagetti poco claro.

Lo último que quiero agregar es que más tarde, incluso si ya no quieres hacer el juego, tienes tu algoritmo A * optimizado que puedes usar para tus próximos juegos. Reusabilidad Por ejemplo, muchos juegos tienen inventario, búsqueda de caminos, generación de procedimientos, interacciones entre npc, sistema de lucha, IA, interacción de interfaz, gestión de entrada. Ahora debes preguntarte: "¿Cuántas veces he reescrito esos elementos desde cero?" Son el 70-80% del juego. ¿Realmente necesitas reescribirlos todo el tiempo? ¿Y qué pasa si no son optimizados?

Candid Moon _Max_
fuente
55
Si el código A * debe comenzar optimizado, ¿qué tan optimizado está "optimizado"? ¿Codifica a mano las cosas en el ensamblaje antes de hacer los puntos de referencia? Creo que, en general, el trabajo no trivial sobre micro optimizaciones debe dejarse hasta que se hayan realizado los puntos de referencia. El compilador de optimización podría hacer un mejor trabajo que usted en micro optimizaciones, y es mucho más barato.
gmatht
@gmatht Bueno, A * puede ser diferente, ya que dije que puede tener un diseño deficiente y no funcionar realmente bien. Pero puede ser multiproceso, según el montón de Fibonacci, tener algunas predicciones, dividirse en trozos. Si estamos hablando de pathfinding, podemos agregar Flow Field para mezclarlo con A *. También alisado, multi altura. Tal vez A * no sea el mejor ejemplo, pero como dije, las optimizaciones no tienen nada que ver con cambiar realmente el código, el desarrollador está cambiando el código porque estaba mal diseñado, por ejemplo, no siguió SOLID como una razón. he escrito este A * bien, ya no necesitas escribirlo.
Candid Moon _Max_
Las micro optimizaciones de @gmatht no son realmente el problema. Creo que OP significa más de la mejor y más eficiente manera de calcular el comportamiento de la IA o el sombreador o cualquier otra cosa.
Candid Moon _Max_
Si sabes cómo escribir estas cosas de manera eficiente, no veo el problema al hacerlo. Tomará la misma cantidad de tiempo o incluso menos. Depende más de la experiencia de los desarrolladores. Como programador, si conozco una mejor solución, no escribiría una mala. Si sé cómo escribir el montón de Fibonacci, no usaré List y sorting para obtener los valores min o max. Por supuesto, tomará más tiempo y esfuerzo escribirlo, en lugar de usar List.Sort (); , pero ¿y si ya tengo uno reutilizable?
Candid Moon _Max_
2
@CandidMoon No estoy de acuerdo con "Tomará la misma cantidad de tiempo o incluso menos". para escribir código eficiente Tal vez tomaría el mismo tiempo escribir código que obviamente no es ineficiente, pero cuando su noción de "eficiencia" significa considerar la contención de caché, agregar múltiples subprocesos, usar intrínsecos específicos de CPU disponibles solo en algunas CPU, cambiar algoritmos para N grande debido a O (N log N) vs O (N): ese tipo de cosas es difícil y propenso a errores y lleva mucho más tiempo que la implementación simple. Solo lo hace cuando sabe que va a afectar materialmente el resultado final.
Michael Anderson