Quando eu estou codificação in-motor, eu estou muitas vezes apenas em causa com um fixo n
: Eu já tenho uma partição espacial limitando o número de objetos de receber update()
, physics()
e render()
para aproximadamente aqueles na tela e áreas circundantes. O tamanho máximo do lote é geralmente bem definido por jogo, embora invariavelmente seja um pouco maior do que você planejou.
Nesse caso, não estou tão preocupado com o grande O quanto com o multiplicador constante de fatores e termos de ordem inferior. Para uma função com tempo de execução como a*n^2 + b*n + c
(o que é O(n^2)
), geralmente estou muito mais preocupado em reduzir a
e possivelmente eliminar c
. Um custo de instalação ou desmontagem c
pode se tornar proporcionalmente grande versus um pequeno n
.
No entanto, isso não significa que big-O (ou mais particularmente big-theta ) seja um ótimo indicador de cheiro de código. Veja um momento O(n^4)
geográfico em algum lugar, ou pior ainda O(k^n)
, e é hora de verificar se você está considerando outras opções.
Em geral, estou muito mais preocupado com a otimização do big O e pulando através de argolas para encontrar algoritmos com big O menor quando estou lidando com ferramentas de criação de dados. Enquanto o número de objetos em um determinado nível / área de streaming geralmente é bem definido, o número total de objetos / objetos de arte / arquivos de configuração / etc em um jogo inteiro pode não ser. Também é um número muito maior. Mesmo executando uma produção de dados paralela, ainda esperamos cerca de um minuto (eu sei, lamentar - a produção de dados para consoles pode levar horas - somos na maioria pequenos jogos portáteis) para passar por um jam data-clean && jam data
ciclo.
Para dar um exemplo específico: isso ficou realmente fora de controle com um algoritmo de fluxo de blocos de segundo plano que transmite blocos de 256 cores 8x8. É útil compartilhar buffers de streaming entre "camadas" de segundo plano, e podemos ter até 6 deles em um determinado nível compartilhando o mesmo buffer. O problema é que estimar o tamanho do buffer necessário é baseado nas posições possíveis de todas as 6 camadas - e se elas são uma taxa de largura / altura / rolagem de número principal, você rapidamente começa a fazer uma pesquisa exaustiva - que começa a se aproximarO(6^numTiles)
- que está na categoria "mais longo que o universo estará" em muitos casos. Felizmente, a maioria dos casos tem apenas 2-3 camadas, mas, mesmo assim, temos mais de meia hora de duração. No momento, amostramos um subconjunto muito pequeno dessas possibilidades, aumentando a granularidade até que um determinado período de tempo tenha passado (ou concluímos a tarefa, o que pode acontecer em pequenas configurações de camada dupla). Aumentamos essa estimativa um pouco com base em estatísticas anteriores de quantas vezes provamos que estamos errados e, em seguida, adicionamos um pouco de preenchimento extra.
Outro exemplo divertido: em um jogo para PC, há algum tempo, o engenheiro-chefe experimentou por um tempo as listas de pulos . A sobrecarga de memória acaba causando mais efeitos de cache, o que adiciona uma espécie de multiplicador não constante a todo o caso - para que não sejam realmente boas escolhas para os pequenos n
. Porém, para listas classificadas maiores, onde as pesquisas são frequentes, elas fornecem um benefício.
(Muitas vezes, acho que o ingênuo algoritmo é maior, O maior, mais rápido em conjuntos de dados menores e mais fáceis de entender; os mais interessantes / complexos (por exemplo, patricia trie) são mais difíceis para as pessoas entenderem e manterem, mas maior desempenho em maior conjuntos de dados.)