Apresentando nosso novo e-book: Pilha de tecnologia orientada a dados (DOTS) da Unity para desenvolvedores avançados

THOMAS KROGH-JACOBSEN / UNITY TECHNOLOGIESSenior Technical Content Marketing Manager
May 30, 2024|9 Min
Apresentando nosso novo e-book: Pilha de tecnologia orientada a dados (DOTS) da Unity para desenvolvedores avançados
Esta página da Web foi automaticamente traduzida para sua conveniência. Não podemos garantir a precisão ou a confiabilidade do conteúdo traduzido. Se tiver dúvidas sobre a precisão do conteúdo traduzido, consulte a versão oficial em inglês da página da Web.

O Data-Oriented Technology Stack (DOTS) da Unitypermite que você crie jogos complexos em larga escala, fornecendo um conjunto de ferramentas de melhoria de desempenho que ajudam você a aproveitar ao máximo seu hardware de destino.

Este e-book de mais de 50 páginas, Introdução à pilha de tecnologia orientada a dados para desenvolvedores avançados do Unity, agora está disponível para download gratuito. Use-o como uma introdução para entender melhor a programação orientada a dados e avaliar se DOTS é a escolha certa para seu próximo projeto. Quer você esteja procurando iniciar um novo projeto baseado em DOTS ou implementar DOTS para partes críticas de desempenho do seu jogo baseado em Monocomportamento, este guia cobre todo o terreno necessário de forma estruturada e clara.

Com o Unity 6 em fase de pré-visualização e o DOTS 1.0 pronto para produção, este é um ótimo momento para explorar as oportunidades que o DOTS traz. O e-book, escrito por Brian Will, engenheiro de software sênior da Unity, se junta aos exemplos atualizados do Unity Learn, ao recente bootcamp do DOTS e aos exemplos do GitHub na coleção de recursos disponíveis para desenvolvedores que desejam aprender a trabalhar com o DOTS.

Um guia para ajudar você a decidir se DOTS é a escolha certa para seu jogo
No Sistema de Componentes de Entidade da Unity, todas as entidades com o mesmo conjunto de tipos de componentes são armazenadas juntas no mesmo “arquétipo”.

Nosso objetivo com este e-book é ajudar você a tomar uma decisão informada sobre se implementar alguns ou todos os pacotes e tecnologias DOTS é a decisão certa para seu projeto Unity existente ou futuro. Cada parte da pilha desempenha um papel na melhoria da velocidade e eficiência de execução de um jogo. O guia tem como objetivo explicar cada uma dessas partes, como elas podem ser usadas em conjunto e sua base comum, o Unity Entity Component System (ECS).

Um dos principais motivos para usar DOTS é obter o máximo desempenho do hardware de destino, e isso requer compreensão de multithreading e alocação de memória. Além disso, para aproveitar o DOTS, você precisará arquitetar seu código e projetos orientados a dados de forma diferente de seus projetos Monobehaviour baseados em C#, com seu nível mais alto de abstração.

Vamos dar uma olhada mais de perto no que você encontrará no e-book.

CTA: Baixe Introdução à pilha de tecnologia orientada a dados para desenvolvedores avançados do Unity.

O que contém o e-book DOTS?
 Uma cena do exemplo Firefighters, disponível no EntityComponentSystemSamples Github

A primeira seção do guia, que incluímos abaixo, apresenta alguns dos fatores que podem contribuir para o baixo desempenho da CPU em um jogo, como sobrecarga de coleta de lixo, dados e códigos que não são amigáveis ao cache, código de máquina gerado pelo compilador abaixo do ideal e muito mais.

A próxima seção explica como cada um dos pacotes e recursos DOTS facilitam a escrita de código que evita armadilhas no desempenho da CPU. Você encontrará explicações úteis para:

  • Sistema de trabalho C#
  • Compilador Burst
  • Coleções
  • Matemática
  • Entidades
  • Gráficos de Entidades
  • Física da Unidade
  • Netcode para Entidades

Após uma análise de cada parte da pilha, você verá uma introdução ao repositório EntityComponentSystemSamples do GitHub, que inclui muitos exemplos que apresentam recursos básicos e avançados do DOTS. Alguns dos exemplos no repositório do Github são reproduzidos em um novo curso do Unity Learn sobre DOTS, Familiarize-se com DOTS.

A outra seção importante no guia DOTS é o apêndice. É aqui que Brian Will fornece explicações detalhadas para conceitos relacionados ao Unity ECS, incluindo alocação de memória e coleta de lixo, memória e cache de CPU, programação multithread, as limitações da programação orientada a objetos e programação orientada a dados.

Excerpt: Sobre desempenho
Um perfil do Unity Profiler mostrando trabalhos compilados em Burst utilizando o potencial da CPU e sendo executados em muitos threads de trabalho.

Se você é um desenvolvedor de jogos experiente, sabe que a otimização de desempenho em plataformas de destino é uma tarefa que ocorre em todo o ciclo de desenvolvimento. Talvez seu jogo tenha um bom desempenho em um PC de última geração, mas e as plataformas móveis de baixo custo que você também está buscando? Os quadros demoram muito mais que os outros, criando problemas perceptíveis? Os tempos de carregamento são irritantemente longos e o jogo trava por segundos inteiros toda vez que o jogador passa por uma porta? Nesse cenário, não apenas a experiência atual é inferior, mas você fica efetivamente impedido de adicionar mais recursos: Mais detalhes e escala do ambiente, mecânicas, personagens e comportamentos, física e plataformas.

Qual é o culpado? Em muitos projetos é a renderização: As texturas são muito grandes, as malhas muito complexas, os shaders muito caros ou há uso ineficaz de loteamento, seleção e LOD.

Outra armadilha comum é o uso excessivo de aceleradores de malha complexos, o que aumenta o custo da simulação física. Ou a simulação do jogo em si é lenta. O código C# que você escreveu que define o que torna seu jogo único pode estar consumindo muitos milissegundos de tempo de CPU por quadro.

Então, como escrever um código de jogo que seja rápido, ou pelo menos não lento?

Nas décadas anteriores, os desenvolvedores de jogos para PC geralmente conseguiam resolver esse problema apenas esperando. Da década de 1970 até o século XXI, o desempenho de CPU single-threaded geralmente dobrava a cada poucos anos (um fenômeno conhecido como Lei de Moore), então um jogo para PC ficaria "magicamente" mais rápido ao longo de seu ciclo de vida. Nas últimas duas décadas, no entanto, os ganhos de desempenho de thread único da CPU foram relativamente modestos. Em vez disso, o número de núcleos na CPU tem crescido e até mesmo pequenos dispositivos portáteis, como smartphones, hoje apresentam vários núcleos. Além disso, a diferença entre dispositivos de jogos de ponta e de baixo custo aumentou, com uma grande parcela da base de jogadores usando hardware com vários anos de uso. Esperar por hardware mais rápido não parece mais uma estratégia viável.

A pergunta a ser feita, então, é: “Por que meu código de CPU é lento em primeiro lugar?” Existem várias armadilhas comuns:

  • A coleta de lixo induz sobrecarga e pausas perceptíveis: Isso ocorre porque o coletor de lixo funciona como um gerenciador automático de memória que gerencia a alocação e a liberação de memória para um aplicativo. A coleta de lixo não só gera sobrecarga de CPU e memória, como também às vezes pausa toda a execução do seu código por muitos milissegundos. Os usuários podem perceber essas pausas como pequenos travamentos ou travamentos mais intrusivos.
  • O código de máquina gerado pelo compilador é subótimo: Alguns compiladores geram código muito menos otimizado do que outros, com resultados variando entre plataformas.
  • Os núcleos da CPU são insuficientemente utilizados: Embora os dispositivos mais baratos de hoje tenham CPUs multi-core, muitos jogos simplesmente mantêm a maior parte de sua lógica no thread principal porque escrever código multithread geralmente é difícil e propenso a erros.
  • Os dados não são amigáveis ao cache: Acessar dados do cache é muito mais rápido do que buscá-los na memória principal. No entanto, acessar a memória do sistema pode exigir que a CPU fique parada e aguarde centenas de ciclos de CPU; em vez disso, você quer que a CPU leia e grave dados do cache o máximo possível. A maneira mais simples de organizar isso é ler e gravar na memória sequencialmente, e assim a maneira mais amigável ao cache de armazenar dados é em matrizes contíguas e compactadas. Por outro lado, se seus dados estiverem espalhados de forma não contígua pela memória, acessá-los normalmente causará muitas perdas de cache dispendiosas; a CPU solicita dados que não estão presentes na memória cache e, em vez disso, precisa buscá-los na memória principal, mais lenta.
  • O código não é amigável ao cache: Quando o código é executado, ele deve ser carregado da memória do sistema, caso ainda não esteja no cache. Uma estratégia é favorecer a chamada de uma função no menor número de lugares possível para reduzir a frequência com que ela deve ser carregada da memória do sistema. Por exemplo, em vez de chamar uma função específica em vários lugares espalhados pelo quadro, é melhor chamá-la em um único loop para que o código só precise ser carregado no máximo uma vez por quadro.
  • O código é excessivamente abstrato: Entre outros problemas, a abstração tende a criar complexidade tanto nos dados quanto no código, o que agrava os problemas mencionados acima: gerenciar alocações sem coleta de lixo se torna mais difícil; o compilador pode não conseguir otimizar com tanta eficiência; multithreading seguro e eficiente se torna mais difícil, e seus dados e código tendem a se tornar menos amigáveis ao cache. Além de tudo isso, as abstrações tendem a distribuir os custos de desempenho, de modo que todo o código fica mais lento, não deixando gargalos claros para otimizar.

Todos os problemas acima são comumente encontrados em projetos Unity. Vejamos isso mais especificamente:

  • Embora o C# permita que você crie objetos alocados manualmente (ou seja, objetos que não são coletados como lixo), a norma padrão no C# e na maioria dos projetos Unity é usar instâncias de classe C#, que são coletadas como lixo. Na prática, os usuários do Unity há muito tempo atenuam esse problema com uma técnica chamada pooling (embora o pooling sem dúvida anule o propósito de usar uma linguagem coletada como lixo em primeiro lugar). O principal benefício do pool de objetos é a reutilização eficiente de objetos de um pool pré-alocado, eliminando a necessidade de criação e desalocação frequentes de objetos.
  • No Editor Unity, o código C# normalmente é compilado para código de máquina com o Compilador Mono. Para compilações autônomas, você pode obter melhores resultados usando IL2CPP (C# Intermediate Language compilada para C++), mas isso traz algumas desvantagens, como tempos de compilação mais longos e dificulta o suporte a mods .
  • É comum que projetos Unity executem todo o seu código no thread principal, em parte porque isso é o que o Unity facilita:
  • As funções de evento do Unity, como o método Update() do MonoBehaviours, são todas executadas no thread principal.
  • A maioria das APIs do Unity só pode ser chamada com segurança a partir do thread principal.
  • Os dados em um projeto típico do Unity tendem a ser estruturados como um monte de objetos aleatórios espalhados pela memória, levando à má utilização do cache. Novamente, isso ocorre em parte porque é isso que o Unity torna fácil:
  • Um GameObject e seus componentes são alocados separadamente, então eles geralmente acabam em partes diferentes da memória.
  • O código em um projeto Unity típico tende a não ser amigável ao cache:
  • As APIs convencionais do C# e do Unity incentivam um estilo de código orientado a objetos, que tende a vários métodos pequenos e cadeias de chamadas complexas. Ao contrário de uma abordagem orientada a dados, ela não é muito amigável ao hardware.
  • As funções de evento de cada MonoBehaviour são invocadas individualmente, e as chamadas não são necessariamente agrupadas por tipo de MonoBehaviour. Por exemplo, se você tiver 1000 MonoBehaviours de Monstros , cada Monstro será atualizado separadamente e não necessariamente junto com os outros Monstros.

O estilo orientado a objetos do C# convencional e de muitas APIs do Unity geralmente levam a soluções com muita abstração. O código resultante tende a ter ineficiências espalhadas por todo o código que são difíceis de separar e isolar.

Para quem é o e-book DOTS?
Uma prévia do novo e-book Introdução à pilha de tecnologia orientada a dados para desenvolvedores avançados do Unity.

Este e-book está disponível gratuitamente para todos, mas é voltado para desenvolvedores Unity que têm experiência com desenvolvimento de jogos orientados a objetos e baseados em Monobehaviour, mas são novos no Unity DOTS e no desenvolvimento de design orientado a dados.

Esperamos que o guia ajude você a entender o DOTS e como esses recursos podem beneficiar seu próximo projeto Unity, além de facilitar que você obtenha o valor total dos exemplos disponíveis em nosso repositório GitHub.