Compressão de dados em jogos online

A compressão de dados teve um renascimento nas últimas duas décadas.

Os ganhos reais de velocidade nas gerações de processadores mais recentes começaram a diminuir em favor de colocar mais núcleos em um chip. Portanto, preencher a lacuna de desempenho do processador / memória tornou-se uma estratégia muito importante para aumentar a escala. Ao mesmo tempo, inovações como Sistemas Numéricos Asimétricos [1] e formatos de fluxo de bits amigáveis ​​para cache aumentaram o limite do que a compactação pode alcançar em um determinado orçamento de tempo a novos patamares. Claro, as demandas também cresceram. Por exemplo, a mudança atualmente em andamento para tornar 4K a resolução de vídeo padrão, substituindo o vídeo 1080p, significa um aumento de quatro vezes mais pixels na tela.

 

Mas e os jogos online? Os pixels na tela e os requisitos de tamanho do disco estão aumentando tão rápido quanto outros jogos. Além disso, um número sem precedentes de jogos agora têm recursos online. No entanto, além de ter que obter os pixels da tela e o som HD dos alto-falantes, os jogos online também precisam obter cada vez mais dados de e para seu (s) servidor (es) e seus pares. A largura de banda disponível está sempre aumentando à medida que o desenvolvimento tecnológico e de infraestrutura continua. A taxa em que isso acontece nunca é rápida o suficiente e varia muito conforme a localidade. Embora um data center possa facilmente dobrar a largura de banda a cada ano, as áreas urbanas rurais e residenciais não têm tanta sorte. A situação se torna ainda mais terrível quando se considera os mercados emergentes que ainda podem ter uma infraestrutura de Internet muito pobre, ainda uma demanda crescente de entretenimento digital. Um indivíduo pode ser capaz de comprar um aparelho decente capaz de rodar os títulos mais recentes, mas nunca poderá instalar uma nova torre de celular ou instalar um cabo submarino para uma boa conexão.

 

E assim, cabe a nós, desenvolvedores de jogos, garantir que somos astutos e cheios de truques para oferecer a melhor experiência possível aos nossos clientes. O teste a seguir nos mostra como usar a compactação de dados para fazer isso em dois casos de uso principais e um caso de uso bônus.

 

Transferências

Oferecer downloads de arquivos não parece ser muito especial à primeira vista: coloque os arquivos em um servidor da Web e amplie com CDN ou serviços de borda, pronto! Todas essas coisas têm custos e esses custos também aumentam. Esses custos geralmente estão associados à quantidade de largura de banda usada, às vezes a quantidade de espaço de armazenamento usado e raramente o número de ciclos de CPU usados. Todas as três taxas podem ser reduzidas aplicando um algoritmo de compressão mais forte aos arquivos.

 

Como os arquivos são compactados apenas uma vez no momento da criação, mas baixados e descompactados muito mais vezes, a velocidade de compactação geralmente é insignificante. Em vez disso, podemos nos concentrar na taxa de compressão e na velocidade de descompressão. Curiosamente, esses dois valores tendem a se correlacionar diretamente até certo ponto, o que significa que uma taxa de compressão mais forte também ajudará na velocidade de descompressão, já que há menos dados para lidar no total. Isso atenua o custo da maior complexidade de algoritmos mais fortes, mas certamente não o elimina. No entanto, a velocidade de descompressão também não é a principal preocupação, porque geralmente é muito mais alta do que a velocidade de transferência da rede pode ser. Isso deixa a taxa de compressão como nosso foco principal, pois qualquer melhoria no tamanho do arquivo se traduzirá diretamente em economia de custos para o cliente e o servidor,

 

O primeiro passo óbvio, mas frequentemente negligenciado, para reduzir os tamanhos de download, é eliminar tudo o que não precisa ser baixado em primeiro lugar. Isso requer um acompanhamento cuidadoso de todos os ativos do jogo e seus usos, o que é uma boa ideia por vários motivos. No contexto de downloads, permite a seleção de dados não utilizados antes mesmo de serem enviados para um servidor. Pode parecer uma maneira fácil de economizar espaço, mas rastrear ativos e suas dependências pode ser uma tarefa assustadora e deve ser considerada desde o primeiro dia. Depois que um projeto atinge milhares de ativos, geralmente é tarde demais.

 

Em segundo lugar, muitos formatos de arquivo já possuem opções de compactação integradas. Os candidatos mais óbvios são os arquivos de imagem e vídeo. Onde a compactação com perdas tem sido o padrão por décadas, a compactação com perdas significa que o conteúdo de um arquivo terá se deteriorado até certo ponto após passar por um ciclo de compactação / descompressão. Quando implementada corretamente, essa perda de dados não causa diferenças perceptíveis no original, ao contrário da cópia analógica de música e fitas VHS, isso acontece apenas na primeira vez que uma certa compressão é aplicada. As passagens de compactação subsequentes não degradarão ainda mais os dados, a menos que uma configuração mais agressiva seja escolhida ou ocorra uma alteração fundamental do algoritmo. No entanto, é uma boa prática manter os arquivos originais por perto e sempre compactá-los.

 

Quanto aos formatos de arquivo de imagem, os mais comumente usados, PNG e JPEG, já são escolhas sólidas. Às vezes, há requisitos que esses formatos não podem atender, como formatos especiais de GPU, mas falaremos mais sobre isso posteriormente. Infelizmente, esses dois formatos de arquivo também são bastante complexos e é importante evitar algumas armadilhas comuns. Por exemplo, os arquivos PNG criados com o Adobe Photoshop contêm muitos metadados que são armazenados como texto simples não compactado, o que pode ocupar mais espaço do que os próprios dados da imagem. Para se livrar dessa e de outras ineficiências que os programas de autoria podem incorrer, o uso de um otimizador PNG como Pngcrush [2] , PNGOUT [3] , AdvanceCOMP [4] ou outros é altamente recomendado.

 

Embora PNG seja frequentemente referido como um formato sem perdas, ele realmente possui um modo com perdas que usa a quantização de cores. Aqui, as cores usadas são armazenadas em uma paleta e, em seguida, referenciadas com um índice por pixel. O número de entradas da paleta é limitado, geralmente a 256, reduzindo assim a quantidade de informações a serem armazenadas, mas também a quantidade de cores exclusivas que acabarão na imagem. A ferramenta (e biblioteca) pngquant [5] faz uso desses modos para produzir imagens ainda menores, mas nem todas as bibliotecas de manipulação de imagens podem lidar com PNGs paletizados.

 

Para JPEG, menos opções estão disponíveis, pois a compressão para troca de fidelidade de imagem já pode ser controlada com precisão com seu parâmetro de qualidade. No entanto, isso não impediu as pessoas de mexer nele desde seu início, há quase 30 anos. Um esforço recente e bem sucedido é Guetzli [6] , pela subsidiária do Google na Suíça. Basta colocar essas ferramentas no pipeline de exportação de ativos, espero que totalmente automatizado, e as imagens que saem do outro lado serão menores.

 

Para arquivos de imagem não cobertos por esses dois formatos de imagem, o formato de arquivo DDS da Microsoft [7] ou KTX do Khronos Group [8] pode ser uma boa escolha, já que é um formato de imagem feito pelo próprio. Nestes casos, uma compressão de propósito geral deve ser aplicada aos dados de pixel. Isso ocorre independentemente do formato escolhido e mesmo quando os pixels estão em um formato compactado por GPU.

 

O auge da compressão de imagens em jogos é o Crunch [9] , e seu sucessor mais poderoso e flexível Basis [10], de Rich Geldreich [11] e Stephanie Hurlburt [12] . Esses kits de ferramentas combinam um algoritmo de compactação personalizado com um formato raster universal amigável para GPU e podem atingir níveis de compactação como JPEG que podem ser carregados diretamente em texturas, com pouco processamento necessário. Há algumas semanas, graças a uma colaboração com o Google, uma versão “leve” do kit de ferramentas está agora disponível [13] para todos.

 

Outros formatos de mídia, como arquivos de vídeo e áudio, também têm esquemas de compactação com perdas, que devem ser usados ​​quando disponíveis. No entanto, ao contrário dos arquivos de imagem, seus formatos de arquivo parecem ter evoluído naturalmente para o armazenamento mais eficiente, de modo que qualquer formato razoavelmente moderno fará um bom trabalho. Parece que há menos coisas que podemos fazer com esses tipos de arquivos de mídia. Freqüentemente, esses formatos também estão vinculados ao software de criação e reprodução usado.

 

É aqui que a disponibilidade de formatos com perdas parece estar secando. Com algumas exceções, como curvas de animação, a maioria dos outros tipos de arquivos tem tolerância de muito pouca a zero para perda de informações, tornando a compactação com perdas inviável. Felizmente, existe uma ampla variedade de formatos de compactação de uso geral sem perdas. O mais comumente usado é o algoritmo Deflate [14] . Conhecido por muitos nomes, é usado em arquivos ZIP, mas também em muitos outros lugares. O algoritmo Deflate nos serviu muito bem, mas também tem cerca de 30 anos agora e a ciência da computação, assim como a tecnologia, avançou muito durante esse tempo.

 

O concorrente provavelmente mais conhecido do Deflate é o algoritmo LZMA [15] , popularizado pelo software de arquivamento 7-zip. Algoritmos de compressão relacionados nesta classe são LZHAM [16] e Bzip2 [17] . Todos eles ainda são amplamente usados ​​para fornecer downloads, mas, como o Deflate, não são mais as melhores opções. Embora o LZMA ainda tenha uma taxa de compressão competitiva, sua velocidade de descompressão é bastante fraca. Então, vamos olhar para alternativas mais modernas.

 

Provavelmente um dos primeiros algoritmos a fazer uso das técnicas descritas no artigo ANS1 mencionado anteriormente é o Zstd [18] de Yann Collet [19] . Yann Collet foi contratado pelo Facebook há algum tempo, mas o algoritmo ainda é gratuito e de código aberto sob uma licença bastante padrão que estranhamente proíbe o uso da marca registrada do Facebook, mas não muito mais. Como um algoritmo de compressão, tem sido um grande sucesso e bate Deflate em relação, velocidade de compressão e velocidade de descompressão. Simplesmente não há razão para não usá-lo.

 

Finalmente, no topo da colina dos algoritmos de compressão de propósito geral está um pacote comercial chamado Oodle [20] . Seus algoritmos de alta compressão Kraken e Leviathan superam qualquer outro algoritmo de compressão disponível atualmente em uma ou mais categorias.

 

Comunicação ao Vivo

 

Ao contrário do caso de uso anterior com relação a downloads, otimizar a comunicação ao vivo é mais complicado. Para downloads, podemos medir facilmente quantos dados precisam ser baixados por cliente, estimar a frequência com que serão baixados e pronto, temos nossa estrutura básica de custos pronta. E para reduzir esses custos reduzindo a quantidade de dados ou reduzindo a frequência com que é necessário fazer o download usando caches locais. Isso é praticamente uma bala de prata.

 

Para comunicação ao vivo, ou seja, todos os tipos de interação bidirecional na rede entre clientes, servidores e entre si, precisamos considerar mais algumas variáveis. Quanto tempo duram as sessões do usuário? Com que frequência as transferências de dados precisam acontecer? Quantos recursos da CPU eu tenho de sobra para preparação e processamento de dados? Quanta largura de banda meus usuários desejam dispensar? A maioria dessas perguntas deve ser respondida nos vários estágios de design de um projeto. Para pelo menos dois deles, a compactação de dados pode ter um impacto significativo: largura de banda e uso de recursos da CPU.

 

Outra complicação é que a taxa de compressão não é mais nosso fator mais importante. A velocidade de compressão (que no caso de download poderíamos ignorar com segurança) e a velocidade de descompressão também desempenham um papel importante. Portanto, é extremamente importante considerar todas as opções disponíveis com cuidado e escolher a mais adequada.

 

Vamos começar com a comunicação via HTTP, onde nossas opções são limitadas pelo framework. Não apenas a gama de métodos de compressão disponíveis é limitada ao que o padrão permite (o que seria bastante amplo), mas também ao que está realmente disponível e é amplamente suportado. Aqui, por muito tempo, gzip [21] foi o único jogo da cidade. Gzip é apenas outro nome para o algoritmo Deflate mencionado anteriormente e, portanto, claramente não é mais a melhor escolha.

 

Felizmente, o Google vem ao resgate com seu algoritmo Brotli [22] de fabricação suíça . Embora Brotli esteja muito alinhado com os algoritmos avançados discutidos na seção anterior, ele tem duas vantagens distintas. Em primeiro lugar, devido à grande influência do Google na infraestrutura da WWW, Brotli se tornou um algoritmo com amplo suporte nos últimos anos. Em segundo lugar, Brotli não é apenas um algoritmo de ponta, ele também vem pré-carregado com um dicionário compartilhado que foi treinado usando uma grande quantidade de tráfego WWW comum. Isso dá ao Brotli uma vantagem adicional quando aplicado para compactar o tipo de arquivo comumente usado em sites.

 

Isso significa que, quando necessário para se comunicar via HTTP, ligue a compactação e solicite ou forneça conteúdo compactado Brotli [23] . Por outro lado, ao servir conteúdo já compactado, desative a compactação HTTP, pois é apenas um desperdício de recursos neste caso. Porém, do ponto de vista do desempenho, é sempre melhor escolher protocolos mais simples que o HTTP, possivelmente até via WebSockets [24] . Porém, antes de chegarmos a isso, um rápido intervalo para falar sobre dicionários e compressão.

 

Um detalhe inevitável ao discutir a compactação de dados é que todos os algoritmos de compactação criam e usam um dicionário. Isso pode acontecer implícita, explicitamente ou de forma mista, mas está sempre lá. Simplesmente falando, esse dicionário contém os fragmentos dos dados originais que aparecem mais de uma vez e são substituídos nos dados compactados por referências de dicionário. Se o dicionário mais as referências forem menores do que os dados originais, a compactação de dados foi obtida.

 

Agora, como já foi aludido na introdução de Brotli, um conjunto relacionado de dados terá dicionários relacionados. Portanto, é possível criar um dicionário compartilhado, que conterá fragmentos de dados comuns a todo o conjunto de dados. Brotli já faz isso com um dicionário definido em sua especificação, mas é claro que é para um conjunto de dados fixo definido pelo Google, que pode não corresponder bem ao nosso conjunto de dados desejado.

 

Não deve ser surpresa que Brotli e Zstd [25] vêm com ferramentas para construir um dicionário compartilhado para qualquer conjunto de dados desejado. Usar isso pode ser muito eficaz ao lidar com pacotes com dados semelhantes. Existe até um padrão web [26] evoluindo para suportar isso, mas não está claro se algum dia chegará ao mainstream. No entanto, se não estivermos limitados por HTTP e no controle da camada de apresentação do protocolo, podemos fazer muito melhor.

 

Antes de começar, é importante considerar quais dados precisam ser transferidos. Todos os dados desnecessários podem ser compactados em nada simplesmente não sendo enviados, não exigindo nenhum algoritmo sofisticado. Portanto, a primeira ordem do dia é garantir que nenhum dado desnecessário seja enviado. Em segundo lugar, os dados devem ser organizados em partes que mudam com frequência e partes que não mudam. Por exemplo, os valores de configuração só precisam ser enviados uma vez no início de uma sessão, e não com todos os pacotes.

 

Conforme discutido na seção de downloads, isso pode parecer óbvio, mas é difícil de fazer na prática. Por exemplo, assim que qualquer tipo de serialização automatizada é introduzida, dados redundantes começam a se infiltrar. Uma maneira de sair disso é rastrear automaticamente o estado que já foi enviado e apenas transferir as alterações necessárias para obter o estado de o receptor atualizado. Fazer isso requer uma conexão persistente e esta é uma área onde o modelo de solicitação / resposta do HTTP pode atingir rapidamente seus limites.

 

Portanto, passamos para o domínio das conexões persistentes, ou seja, TCP / IP. O protocolo exato não importa, desde que tenhamos um canal de comunicação persistente entre dois endpoints que estão disponíveis durante uma sessão que não perde ou reordena os pacotes. Com essa garantia, podemos tirar outro truque da cartola.

 

A compactação tende a ficar melhor quando pedaços maiores de dados estão sendo compactados, porque a chance de encontrar fragmentos de dados repetidos aumenta. Para comunicações ao vivo, no entanto, queremos enviar pacotes de dados que sejam os menores possíveis, então perdemos muitas oportunidades de compressão. A maneira de contornar isso é comunicar-se usando um fluxo compactado de dados, o que é obtido ao não redefinir o estado do par compressor / descompressor após cada pacote. Desta forma, o algoritmo pode se referir a fragmentos de dados enviados em pacotes anteriores, aproximando-se da eficácia da compactação de grandes blocos de dados.

 

Todos os algoritmos de compactação podem fazer isso em princípio, mas nem todas as APIs de compactação o suportam. Se for possível usar a compactação de fluxo de alguma forma, é aconselhável fazer uso dela. É mais eficaz e menos complicado do que a abordagem de dicionário compartilhado, porque o dicionário compartilhado que se ajusta aos dados reais sendo transferidos em uma sessão é criado implicitamente ao longo do tempo.

 

Agora, vamos examinar alguns algoritmos de compressão concretos. Brotli e Zstd suportam compressão de fluxo, então eles já são boas escolhas. Mas eles não são os melhores em velocidade de compressão, o que costuma ser uma preocupação na comunicação ao vivo. Os dados são sempre gerados dinamicamente e precisam ser compactados / descompactados toda vez que são enviados pelo remetente e pelo destinatário.

 

Nos últimos anos, foi desenvolvido um conjunto de algoritmos que fornecem uma taxa de compactação decente, mas compactam e descompactam muito mais rápido. Alguns deles, na configuração certa, podem exceder a velocidade de apenas copiar a mesma quantidade de dados na memória, tornando realmente mais lento para não usar a compactação.

 

Um dos algoritmos mais antigos dessa classe é o LZO [27] , e está disponível sob uma licença comercial e uma licença livre. Yann Collet, famoso pelo Zstd, criou o LZ4 [28] , que é totalmente gratuito e normalmente se sairá melhor do que o LZO. Outro conjunto de algoritmos livres está disponível em Density [29] , mas o rei da colina é novamente o pacote Oodle16 com seus algoritmos Selkie e Mermaid. Qualquer um desses algoritmos provavelmente acelerará a comunicação de sua vida, reduzirá a largura de banda usada e, como bônus, fornecerá algum ofuscamento.

 

Embora a ofuscação por si só não seja nenhuma garantia de segurança, ela ajuda na eficiência da criptografia porque, simplesmente falando, os dados compactados são matematicamente mais aleatórios. Neste ponto, deve ser mencionado que os dados criptografados, pelo design do algoritmo de criptografia, se forem bons, não podem mais ser compactados. Portanto, a criptografia deve sempre ser aplicada após a compactação.

 

Uma palavra de cautela em relação à compressão de streaming. Se o número de conexões em um único host for muito alto, a quantidade de memória necessária para o rastreamento do estado de compactação pode chegar a um número significativo. Se for esse o caso, um estudo cuidadoso da documentação dos algoritmos de compressão usados ​​ou API geralmente revelará maneiras de reduzir isso, ao custo da taxa de compressão, é claro. Também pode ser necessário mudar o algoritmo usado para um com um requisito de memória de estado menor.

 

Isso nos leva nitidamente a outro ponto que vale a pena considerar. Como temos tantas opções de compactação boas disponíveis agora, faz sentido integrar mais de uma delas e tornar seu uso configurável, talvez até por sessão. Isso é definitivamente útil durante o desenvolvimento para medir vários algoritmos em várias configurações com dados reais para ver qual é o mais eficiente. Em segundo lugar, pode haver situações durante a operação em que uma troca de algoritmo seja benéfica, por exemplo, quando um DOS ou vetor de travamento é descoberto no algoritmo de compressão. Existem bibliotecas, como o Squash [30] , disponíveis que já fornecem uma interface comum para vários algoritmos de compressão.

 

O que nos leva à questão da segurança. A maioria dos algoritmos modernos mencionados aqui é criada ou apoiada por empresas com interesse financeiro na segurança de seus produtos e com um bom histórico de fazê-lo. Todos eles podem ser considerados seguros para uso geral. Para qualquer necessidade ou dúvida específica nessa área é sempre aconselhável entrar em contato diretamente com os fornecedores desses algoritmos.

 

Bônus: streaming

O streaming de áudio e vídeo é atualmente o setor de crescimento mais rápido na mídia interativa e em outras mídias, e por um bom motivo. Sem o uso pesado de compactação de dados, não seria possível fazer nada disso. Infelizmente, o campo de streaming de mídia atualmente tem uma barreira de entrada muito alta, mas podemos aprender algumas coisas com isso.

 

A mídia de streaming é sensível ao tempo e tem pacotes de dados bastante grandes para transferir. Essa sensibilidade ao tempo pode ser tratada de duas maneiras: pausar o stream ou pular partes dele. A pausa só é possível para streaming não interativo e não ao vivo, como um stream de filme. Para qualquer tipo de mídia interativa, os dados que não chegam a tempo não são mais necessários e podem ser descartados. O mesmo é verdade para jogos em tempo real, como jogos de tiro ou de estratégia em tempo real, aliás. Isso significa que, ironicamente, a compactação de streaming não é possível para toda a duração, porque um fluxo de dados contíguo não pode ser garantido.

 

Para atingir uma taxa de compressão decente, simplesmente falando, esses arquivos de mídia são divididos em pedaços que são compostos de alguns segundos de conteúdo, que são então compactados individualmente e servidos ao cliente. Dessa forma, pedaços inteiros podem ser ignorados, se necessário. Além disso, os blocos podem ser preparados em vários níveis de qualidade, permitindo ajustar a largura de banda em tempo real.

 

Conclusões

 

Como vimos, existem muitas opções disponíveis que podem fazer uma melhoria significativa em relação a deixar os dados descompactados ou usar Deflate. Escolher a melhor opção para um determinado problema nem sempre é simples, mas existem alguns ganhos garantidos. Por exemplo, substituir Deflate por Zstd ou Brotli é quase sempre um resultado líquido positivo e não incorre em custos além da implementação da mudança. Outras mudanças podem ser mais complexas e exigir uma boa quantidade de experimentação, mas também permitir benefícios maiores.

 

Lembre-se de que cada bit de largura de banda economizado é uma economia igual no custo de largura de banda. E esse custo rapidamente se transforma no principal custo não relacionado a pessoal à medida que um produto ou serviço aumenta. Na verdade, certos casos de uso, como streaming de vídeo, são totalmente inviáveis ​​técnica ou financeiramente sem o tratamento de dados adequado.

 

Além da economia de custos potencialmente substancial, o uso inteligente da compactação de dados também pode reduzir os tempos de espera e o uso de memória, enquanto abre oportunidades para fornecer conteúdo de maior qualidade com a mesma largura de banda ou conteúdo da mesma qualidade com largura de banda reduzida. Ele permite que um produto se destaque de sua concorrência. E na indústria do entretenimento, isso pode valer muito mais do que qualquer economia de custo.

 

 

O Avance Network é uma comunidade fácil de usar que fornece segurança de primeira e não requer muito conhecimento técnico. Com uma conta, você pode proteger sua comunicação e seus dispositivos. O Avance Network não mantém registros de seus dados; portanto, você pode ter certeza de que tudo o que sai do seu dispositivo chega ao outro lado sem inspeção.


Strong

5178 Blog Postagens

Comentários