Atualmente, estou no estágio de design de uma arquitetura baseada em componentes em C ++.
Meu design atual inclui o uso de recursos como:
std::vector
s destd::shared_ptr
s para segurar os componentesstd::dynamic_pointer_cast
std::unordered_map<std::string,[yada]>
Os componentes representam dados e lógica de vários itens necessários em um software semelhante a um jogo, como Gráficos, Física, IA, Áudio, etc.
Eu já li em todo lugar que as falhas de cache são difíceis de executar, por isso executei alguns testes, o que me levou a acreditar que, de fato, ele pode atrasar um aplicativo.
Não pude testar os recursos de linguagem mencionados anteriormente, mas é dito em muitos lugares que eles tendem a custar muito e devem ser evitados, se possível.
Como estou no estágio de design da arquitetura, e estes serão incluídos no núcleo do design, devo tentar encontrar maneiras de evitá-los agora, já que será muito difícil alterá-lo mais tarde, se houver desempenho problemas?
Ou sou pego fazendo otimização prematura?
fonte
the answer to this question is almost always a resounding "YES !!"
. Eu ainda acho que é melhor fazê-lo funcionar primeiro e otimizar depois, mas YMMV, todos têm sua opinião, todos válidos, e somente o OP pode realmente responder à sua própria pergunta - subjetiva.Respostas:
Sem ler nada além do título: Sim.
Depois de ler o texto: Sim. Embora seja verdade que mapas e ponteiros compartilhados etc. não tenham um bom desempenho em cache, você certamente descobrirá que o que deseja usá-los - até onde eu entendo - não é o gargalo e não será mantido ou use o cache de forma eficiente, independentemente da estrutura de dados.
Escreva o software evitando os erros mais estúpidos, depois teste, encontre os gargalos e otimize!
Fwiw: https://xkcd.com/1691/
fonte
Eu não estou familiarizado com C ++, mas em geral depende.
Você não precisa otimizar prematuramente os algoritmos isolados, onde é possível otimizar facilmente quando se trata disso.
No entanto, você precisa obter o design geral do aplicativo para alcançar os principais indicadores de desempenho desejados.
Por exemplo, se você precisar projetar um aplicativo para atender a milhões de solicitações por segundo, precisará pensar na escalabilidade do aplicativo ao projetá-lo, em vez de fazê-lo funcionar.
fonte
Se você precisar perguntar, sim. Otimização prematura significa otimização antes de ter certeza de que há um problema de desempenho significativo.
fonte
ECS? Na verdade, sugiro que talvez não seja prematuro pensar muito no lado orientado a dados do design e comparar diferentes representantes, pois isso pode afetar seus designs de interface , e é muito caro mudar tarde no final. o jogo. Além disso, o ECS exige muito trabalho e pensamento antecipado, e acho que vale a pena utilizar parte desse tempo para garantir que isso não traga luto por desempenho no nível de design mais adiante, considerando como ele estará no coração de sua empresa. mecanismo todo enlouquecido. Esta parte me brilha:
Mesmo com otimizações de string pequenas, você tem um contêiner de tamanho variável (strings) dentro de outro contêiner de tamanho variável (unordered_maps). Na verdade, as pequenas otimizações cordas realmente pode ser tão prejudicial quanto útil, neste caso, se a tabela é muito escassa, uma vez que a pequena otimização corda implicaria que cada índice não utilizado da tabela de hash ainda vai usar mais memória para a otimização SS (
sizeof(string)
seria ser muito maior) até o ponto em que a sobrecarga total de memória da sua tabela de hash pode custar mais do que o que você está armazenando nela, especialmente se for um componente simples como um componente de posição, além de gerar mais falhas de cache com grande avanço para passar de uma entrada na tabela de hash para a próxima.Estou assumindo que a string é algum tipo de chave, como uma identificação de componente. Nesse caso, isso já torna as coisas dramaticamente mais baratas:
... se você quiser os benefícios de poder ter nomes fáceis de usar que os scripts podem usar, por exemplo, as seqüências internas podem oferecer o melhor dos dois mundos aqui.
Dito isto, se você pode mapear a string para um intervalo razoavelmente baixo de índices densamente usados, poderá fazer isso:
A razão pela qual não considero prematuro é porque, novamente, isso pode afetar seus designs de interface. O objetivo do DOD não deve ser o de tentar apresentar as representações de dados mais eficientes imagináveis de uma só vez na IMO (que geralmente devem ser alcançadas iterativamente, conforme necessário), mas pensar nelas o suficiente para projetar interfaces no topo para trabalhar com isso. dados que deixam espaço suficiente para o perfil e a otimização sem alterações de design em cascata.
Como um exemplo ingênuo, um software de processamento de vídeo que combina todo o seu código com isso:
Não vai chegar muito longe sem uma reescrita potencialmente épica, já que a ideia de abstrair no nível de pixel único já é extremamente ineficiente (a
vptr
própria custaria muitas vezes mais memória que o pixel inteiro) em comparação com a abstração no nível da imagem (o que geralmente representam milhões de pixels). Então, pense bastante em suas representações de dados com antecedência, para que você não precise enfrentar um cenário de pesadelo e, idealmente, não mais, mas aqui acho que vale a pena pensar sobre essas coisas desde que você não queira criar um mecanismo complexo em torno do seu ECS e descubra que o próprio ECS é o gargalo de maneiras que exigem que você mude as coisas no nível do design.Quanto às falhas de cache do ECS, na minha opinião, os desenvolvedores geralmente se esforçam demais para tornar seus ECS compatíveis com o cache. Começa a render muito pouco esforço para tentar acessar todos os seus componentes de maneira perfeitamente contígua e geralmente implica em copiar e embaralhar dados em todo o lugar. Geralmente, é bom o suficiente, digamos, apenas modificar índices de componentes de classificação antes de acessá-los, para que você os acesse de uma maneira em que pelo menos não esteja carregando uma região de memória em uma linha de cache, apenas para despejá-la e carregá-la. tudo de novo no mesmo loop apenas para acessar uma parte diferente da mesma linha de cache. E um ECS não precisa fornecer eficiência incrível em todos os aspectos. Não é como se um sistema de entrada se beneficiasse tanto quanto um sistema de física ou renderização, então eu recomendo apontar para "bom" eficiência global e "excelente" apenas nos locais onde você realmente precisa. Dito isto, o uso de
unordered_map
estring
aqui são fáceis o suficiente para evitar.fonte