Cuando estoy de codificación en el motor, estoy a menudo sólo se refiere a un fijo n
: Ya tengo una partición espacial limitar el número de objetos que recibe update()
, physics()
yrender()
aproximadamente a las de la pantalla y las áreas circundantes. El tamaño máximo de lote generalmente está bastante bien definido por juego, aunque invariablemente es un poco más grande de lo que has planeado.
En este caso, no estoy tan interesado en big-O como en el multiplicador de factor constante y los términos de orden inferior. Para una función con tiempo de ejecución como a*n^2 + b*n + c
(que es O(n^2)
), a menudo estoy mucho más preocupado por reducir a
y posiblemente eliminar c
. Un costo de instalación o desmontaje c
puede ser proporcionalmente grande frente a uno pequeño n
.
Sin embargo, esto no quiere decir que big-O (o más particularmente big-theta ) es un gran indicador de olor de código. Vea un O(n^4)
lugar, o peor aún, un O(k^n)
tiempo geométrico, y es hora de asegurarse de que está considerando otras opciones.
En general, estoy mucho más preocupado por la optimización de Big-O y saltar a través de aros para encontrar algoritmos con Big-O más bajo cuando se trata de herramientas de creación de datos. Si bien el número de objetos en un nivel / área de transmisión en general generalmente está bien definido, el número total de objetos / activos de arte / archivos de configuración / etc. en un juego completo puede no estarlo. También es un número mucho mayor. Incluso ejecutando una marca de datos paralela, seguimos esperando el orden de un minuto (lo sé, quejarse, la creación de datos para consolas puede llevar horas, en su mayoría son pequeños juegos portátiles) para pasar por un jam data-clean && jam data
ciclo.
Para dar un ejemplo específico: esto realmente se salió de control con un algoritmo de transmisión de mosaicos de fondo que transmite mosaicos de 8x8 de 256 colores. Es útil compartir búferes de transmisión entre "capas" de fondo, y podríamos tener hasta 6 de ellos en un nivel dado que compartan el mismo búfer. El problema es que estimar el tamaño del búfer necesario se basa en las posibles posiciones de las 6 capas, y si son un ancho / alto / velocidad de desplazamiento del número primo, rápidamente comienza a realizar una búsqueda exhaustiva, que comienza a acercarseO(6^numTiles)
- que se encuentra en la categoría "más largo que el universo estará alrededor" en muchos casos. Afortunadamente, la mayoría de los casos son solo de 2 a 3 capas, pero aun así, superamos la media hora de tiempo de ejecución. Por el momento, tomamos muestras de un subconjunto muy pequeño de estas posibilidades, aumentando la granularidad hasta que haya transcurrido una cantidad de tiempo establecida (o hayamos completado la tarea, lo que puede suceder para configuraciones pequeñas de doble capa). Aumentamos un poco esta estimación en función de las estadísticas anteriores de la frecuencia con la que hemos demostrado que estamos equivocados, y luego agregamos un poco de relleno adicional para una buena medida.
Otro ejemplo divertido: en un juego de PC hace un tiempo, el ingeniero principal experimentó durante un tiempo con listas de omisión . La sobrecarga de memoria termina causando más efectos de caché, lo que agrega una especie de multiplicador no constante a todo el asunto, por lo que realmente no son una buena opción para los pequeños n
. Pero para listas ordenadas más grandes donde las búsquedas son frecuentes, proporcionan un beneficio.
(A menudo encuentro que el algoritmo ingenuo es mayor-O grande, más rápido en conjuntos de datos más pequeños y más fácil de entender; los más interesantes / complejos (por ejemplo, patricia trie) son más difíciles de entender y mantener para las personas, pero un mayor rendimiento en grandes conjuntos de datos).