O que você está procurando?
Engine & platform

Melhorando o desempenho da pesquisa do Burst Inspector

JONAS REHOLT / UNITY TECHNOLOGIESStudent Software Developer
Jul 11, 2023|7 Min
Melhorando o desempenho da pesquisa do Burst Inspector
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.

Meu nome é Jonas Reholt e sou um estudante que trabalha na equipe Burst . Estou acessando o blog para compartilhar minha jornada de otimização que ajudou a tornar possíveis as recentes mudanças de desempenho no Burst Inspector. A pesquisa do Burst Inspector agora é 13 vezes mais rápida, permitindo que os desenvolvedores se concentrem mais rapidamente no código com o qual se importam ao otimizar projetos.

Continue lendo para saber como você pode usar o Unity Profiler para investigar gargalos de desempenho em seu programa e como corrigi-los.

Introdução ao Burst Inspector

O compilador Unity Burst transforma seu código C# em código assembly altamente otimizado. O Burst Inspector permite que você inspecione o código do assembly diretamente no Unity Editor, para que você não precise usar ferramentas externas para uma inspeção simples do código.

Ao abrir o Burst Inspector pela primeira vez e selecionar um trabalho de destino para exibir, você verá uma janela semelhante à imagem abaixo.

A janela Burst Inspector mostrando o código de montagem compilado de um trabalho de destino Burst
A janela Burst Inspector mostrando o código de montagem compilado de um trabalho de destino Burst

Como você pode ver, o Burst Inspector fornece destaque de sintaxe, setas de fluxo de ramificação e muito mais.

O inspetor tentará rolar até o assembly que está implementando a função de destino escolhida, mas também é útil pesquisar na visualização do assembly por instruções específicas, comentários, etc. Isso nos leva ao tópico deste post do blog.

Melhorando o desempenho da pesquisa de texto

Para executar a pesquisa, o inspetor precisa pesquisar a saída do assembly original e transformar esses índices em posições na visualização do inspetor. A funcionalidade de pesquisa original seguiu o padrão mostrado abaixo e dependia muito da implementação de System.String.IndexOf(*).

while (assemblyCode.IndexOf(key, accIdx) >= 0) {
// ...	
// Do logic for handling search hits
// ...
}

Executar a pesquisa acima em 135.582 linhas de código assembly para um resultado de pesquisa comum (21.769 resultados no total) resultou em um tempo de execução de cerca de 12 segundos para a primeira pesquisa e cerca de 5 segundos para pesquisas subsequentes. Esse não é realmente um tempo de espera desejável para um evento de GUI , então tivemos que fazer algo. Executar a pesquisa no Unity Profiler revelou que 37,3% do tempo de execução foi gasto em IndexOf(*), conforme visto abaixo.

Execução de perfil de busca de uma string comum no Burst Inspector
Execução de perfil de busca de uma string comum no Burst Inspector

Uma otimização sensata precisa abordar a dependência dessa função, seja fazendo uma implementação personalizada ou alterando o algoritmo completamente. Não importa qual algoritmo seja usado, ele envolverá percorrer toda a sequência de caracteres. Portanto, é necessária alguma implementação personalizada para encontrar correspondências. Diante disso, pareceu adequado iniciar a otimização mantendo o algoritmo original, mas criando uma função IndexOf personalizada.

Os 3,34 segundos gastos em LongTextArea.GetFragNrFromBlockIdx() decorrem da recuperação de código assembly não colorido. Isto é usado para realizar a pesquisa. O Burst Inspector atualmente salva o código de montagem duas vezes: uma vez formatado para renderização e uma vez não formatado.

Escrever uma função personalizada também tem o bom efeito colateral de reduzir o número de chamadas, já que atualmente há uma chamada para cada resultado de pesquisa, mais uma.

O código-fonte de IndexOf(*) revela muitas verificações de segurança necessárias para uma implementação geral robusta. Entretanto, no nosso caso, podemos assumir com segurança que a maioria dessas verificações são verdadeiras. Para tentar extrair cada gota de desempenho, você vai querer criar uma função semelhante à C para evitar coisas como verificação de limites.

Você pode escrever a função seguindo o pseudocódigo abaixo, onde IsKeyMatch(*) simplesmente verifica se a chave corresponde ou não.

List<int> Search(string assemblyCode, string key, int accIdx) {
     var hits = new List<int>();
     for (i = accIdx; i < assemblyCode.len - key.len; i++) {
	     if (IsKeyMatch(assemblyCode, key, i)) {
		     hits.add(i);
	     	     i += key.len-1;
          }
     }
     return hits;
}

Entretanto, como C# é uma linguagem gerenciada, essa função semelhante a C exige que você fixe os objetos gerenciados usados para que o coletor de lixo não realoque o endereço de memória. Aqui está o código padrão:

unsafe {
	fixed (char* source = assemblyCode) {
		fixed (char* needle = key) {
			CustomIndexOf(source, key)
		}
	}
}

Juntar essas coisas permite que você separe o loop while original em uma única chamada para o localizador de índices e a lógica para manipular os resultados da pesquisa:

matches = FindAllMathces(text, key)
foreach match {
	...
	Do logic for handling search hits
	...
}

Quais foram os ganhos? Usando o pequeno exemplo anterior, essa alteração no código resulta em uma aceleração de 6,6x na chamada inicial e de 13,2x nas chamadas subsequentes (medidas como antigo/novo). A menor aceleração na pesquisa inicial decorre da sobrecarga de carregamento no assembly não formatado para evitar encontrar correspondências em sequências de cores.

Medições de tempo de execução da pesquisa de texto no Burst Inspector
Medições de tempo de execução da pesquisa de texto no Burst Inspector

Com essas melhorias, pesquisas pesadas com pouco menos de 22.000 resultados agora levarão cerca de 1,8 segundos para a pesquisa inicial e cerca de 0,4 segundos para pesquisas subsequentes. Isso torna o Burst Inspector mais útil para grandes montagens, já que não há mais tempo suficiente para fazer uma xícara de chá durante cada pesquisa.

Você pode aproveitar essa melhoria de desempenho agora com o pacote Burst 1.8.7.

Quer saber mais sobre Burst? Entre em contato conosco no fórum Burst . Não deixe de ficar atento a mais novos blogs técnicos de outros desenvolvedores do Unity como parte do processo contínuo SérieTech from the Trenches.