Componentes do Tempo de Execução do Windows e otimização de interoperabilidade

Crie aplicativos do Windows que usam componentes do Windows Runtime e interoperabilidade entre tipos nativos e gerenciados, evitando problemas de desempenho de interoperabilidade.

Práticas recomendadas de interoperabilidade com componentes do Windows Runtime

Se não houver cautela, o uso de componentes do Windows Runtime poderá ter um grande impacto no desempenho do seu aplicativo. Esta seção discute como obter bom desempenho quando seu aplicativo usa componentes do Windows Runtime.

Introdução

Interoperabilidade pode ter um grande impacto no desempenho, e talvez você a esteja usando sem perceber. O Windows Runtime manipula grande parte da interoperabilidade, para que você possa ser mais produtivo e reutilizar códigos escritos em outras linguagens. É recomendável tirar proveito do que o Windows Runtime faz por você, mas tenha em mente que isso pode afetar o desempenho. Esta seção discute o que pode ser feito para diminuir o impacto que a interoperabilidade tem no desempenho do seu aplicativo.

O Windows Runtime tem uma biblioteca de tipos que estão acessíveis em qualquer linguagem na qual se possa escrever um aplicativo na Plataforma Universal do Windows. Use os tipos do Windows Runtime em C# ou Microsoft Visual Basic da mesma forma que usa objetos .NET. Não é preciso fazer chamadas de método de invocação de plataforma para acessar os componentes do Windows Runtime. Isso torna a escrita dos seus aplicativos menos complexa, mas é importante observar que pode haver mais interoperabilidade do que o esperado. Se um componente do Windows Runtime for escrito em uma linguagem diferente de C# ou Visual Basic, os limites da interoperabilidade serão transpostos quando você o utilizar. Transpor os limites da interoperabilidade pode afetar o desempenho de um aplicativo.

Quando você desenvolve um aplicativo da Plataforma Universal do Windows em C# ou Visual Basic, os dois conjuntos de APIs mais comuns usados são as APIs do Windows Runtime e as APIs do .NET para aplicativos da UWP. Em geral, os tipos fornecidos pelo Windows que são compilados no Windows Runtime em namespaces que começam com "Windows." e os tipos .NET estão em namespaces que começam com "System". Mas há exceções. Os tipos em .NET para aplicativos UWP não exigem interoperabilidade quando são usados. Se você achar que tem mau desempenho em uma área que usa o Windows Runtime, poderá usar .NET para aplicativos UWP para obter melhor desempenho.

Observação: a maior parte dos componentes do Windows Runtime fornecidos com o Windows 10 é implementada em C++, para que você possa transpor os limites da interoperabilidade ao usá-los em C# ou Visual Basic. Como sempre, avalie seu aplicativo para observar se o uso de componentes do Windows Runtime afeta o desempenho do aplicativo antes de investir em alterações do código.

Neste tópico, quando mencionamos "componentes do Windows Runtime", estamos nos referindo a componentes do Windows Runtime escritos em linguagem diferente do C# ou do Visual Basic.

 

Sempre que você acessa uma propriedade ou chama um método em um componente do Windows Runtime, há um custo de interoperabilidade. Na verdade, o custo de criar um componente do Windows Runtime é superior ao de criar um objeto .NET. Isso acontece porque o Windows Runtime deve executar um código que transite entre a linguagem do seu aplicativo e a do componente. Além disso, se você passar dados para o componente, eles devem ser convertidos entre tipos gerenciados e não gerenciados.

Usar componentes do Windows Runtime com eficiência

Se você achar que precisa melhorar o desempenho, poderá verificar se o seu código usa componentes do Windows Runtime da forma mais eficiente possível. Esta seção apresenta algumas dicas para melhorar o desempenho ao usar componentes do Windows Runtime.

O impacto sobre o desempenho só se torna perceptível após um número significativo de chamadas em um curto período. Um aplicativo bem projetado que encapsule chamadas para componentes do Windows Runtime da lógica de negócios e de outro código gerenciado não deve apresentar altos custos de interoperabilidade. No entanto, se os seus testes indicarem que o uso de componentes do Windows Runtime está afetando o desempenho do aplicativo, as dicas aqui apresentadas ajudarão a aprimorar o desempenho.

Considere o uso de tipos fornecidos pelo .NET para aplicativos UWP

Há certos casos em que você pode realizar uma tarefa usando o tipo Windows Runtime ou um tipo fornecido pelo .NET para aplicativos UWP. É recomendável tentar não misturar tipos .NET e Windows Runtime. Tente permanecer em um ou outro. Por exemplo, você pode analisar um fluxo de xml usando o tipo Windows.Data.Xml.Dom.XmlDocument (um tipo Windows Runtime) ou o tipo System.Xml.XmlReader (um tipo .NET). Use a API proveniente da mesma tecnologia que o fluxo. Por exemplo, se você ler xml de um MemoryStream, use o tipo System.Xml.XmlReader, pois ambos são tipos .NET. Caso você faça a leitura por meio de um arquivo, use o tipo Windows.Data.Xml.Dom.XmlDocument, pois as APIs do arquivo e o XmlDocument são implementados em componentes nativos do Windows Runtime.

Copiar objetos do Windows Runtime para tipos .NET

Quando um componente do Windows Runtime retorna um objeto do Windows Runtime, pode ser benéfico copiar o objeto retornado em um objeto .NET. Dois momentos em que isso é especialmente importante ocorrem no trabalho com coleções e fluxos.

Se você chamar uma API do Windows Runtime que retorna uma coleção e salvar e acessar essa coleção muitas vezes, talvez seja benéfico copiar a coleção em uma coleção .NET e, daí em diante, usar a versão .NET.

Armazenar em cache os resultados das chamadas a componentes do Windows Runtime para uso posterior

Você pode obter melhor desempenho salvando os valores como variáveis locais em vez de acessar várias vezes um tipo Windows Runtime. Isso pode ser muito benéfico se você usar um valor dentro de um loop. Avalie sua aplicação para observar se o uso de variáveis locais aprimora o seu desempenho. O uso de valores armazenados em cache pode aumentar a velocidade do aplicativo, pois o tempo gasto em interoperabilidade será menor.

Combinar chamadas a componentes do Windows Runtime

Tente concluir as tarefas com o menor número possível de chamadas a objetos UWP. Por exemplo, normalmente é melhor ler um grande volume de dados de um fluxo do que pequenos volumes por vez.

Use APIs que agrupem o trabalho no menor número possível de chamadas, em vez de APIs que executem menos trabalho e exijam mais chamadas. Por exemplo, prefira criar um objeto chamando construtores que inicializem várias propriedades de uma vez, no lugar de chamar o construtor padrão e atribuí-las uma por vez.

Compilar componentes do Windows Runtime

Caso você tenha escrito um componente do Windows Runtime que possa ser usado por aplicativos escritos em C++ ou em JavaScript, verifique se o componente foi projetado para obter bom desempenho. Todas as sugestões com vistas ao bom desempenho nas aplicações são extensivas aos componentes. Avalie seu componente para observar quais APIs têm altos padrões de tráfego e, nessas áreas, considere o fornecimento de APIs que permitam aos seus usuários trabalhar com poucas chamadas.

Manter seu aplicativo rápido ao usar a interoperabilidade em código gerenciado

O Windows Runtime facilita a interoperabilidade entre o código nativo e o gerenciado, mas, se você não tiver cuidado, poderá haver custos de desempenho. Consulte como obter bom desempenho ao usar a interoperabilidade em seus aplicativos UWP gerenciados.

O Windows Runtime permite que os desenvolvedores escrevam aplicativos em XAML usando a linguagem que eles preferem, graças às projeções das APIs do Windows Runtime disponíveis em cada linguagem. Quando um aplicativo é desenvolvido em C# ou Visual Basic, essa conveniência tem um custo de interoperabilidade, pois as APIs do Windows Runtime são geralmente implementadas no código nativo, e qualquer invocação do Windows Runtime no C# ou no Visual Basic exige que o CLR faça a transição de um registro de ativação gerenciado para um nativo e realize marshaling dos parâmetros de funções para representações acessíveis pelo código nativo. Essa sobrecarga é irrisória para a maioria dos aplicativos. Mas quando você faz muitas chamadas (de centenas de milhares a milhões) a APIs do Windows Runtime no caminho crítico de um aplicativo, esse custo pode se tornar perceptível. Em resumo, você quer ter certeza de que o tempo gasto na transição entre as linguagens seja curto, em comparação com o tempo gasto na execução do resto do seu código. Isso é mostrado no diagrama a seguir.

Interop transitions should not dominate the program execution time.

Os tipos listados em .NET for Windows apps não têm esse custo de interoperabilidade quando usados a partir do C# ou do Visual Basic. Como regra geral, você pode intuir que os tipos em namespaces que começam com “Windows.” fazem parte do conjunto de APIs do Windows Runtime fornecido pelo Windows, e que os tipos em namespaces que começam com "System." são tipos .NET. Até mesmo a utilização simples de tipos Windows Runtime acarreta custos de interoperabilidade para alocação e acesso à propriedade.

Meça seu aplicativo e determine se a interoperabilidade está ocupando uma parte considerável do tempo de execução dos seus aplicativos, antes de otimizar seus custos de interoperabilidade. Ao analisar o desempenho do seu aplicativo com o Visual Studio, você pode obter uma estimativa dos custos de interoperabilidade usando o modo de exibição Funções e consultando o tempo inclusivo gasto em métodos que chamam o Windows Runtime.

Se seu aplicativo está lento devido à sobrecarga de interoperabilidade, você pode aprimorar o desempenho dele reduzindo as chamadas a APIs do Windows Runtime em caminhos de código executados intensivamente. Por exemplo, um mecanismo de jogo que esteja executando uma grande quantidade de cálculos de física consultando constantemente a posição e as dimensões de UIElements pode poupar muito tempo armazenando as informações necessárias de UIElements em variáveis locais, fazendo cálculos nesses valores armazenados em cache e atribuindo o resultado final novamente a UIElements após a conclusão dos cálculos. Outro exemplo: se uma coleção é intensamente acessada por código em C# ou Visual Basic, é mais eficiente usar uma coleção do namespace System.Collections, em vez de uma coleção do namespace Windows.Foundation.Collections. Você também pode considerar o uso de chamadas combinadas a componentes do Windows Runtime, por exemplo, com o uso de APIs Windows.Storage.BulkAccess.

Criando um componente UWP

Caso você tenha escrito um componente do Windows Runtime para ser usado em aplicativos escritos em C++ ou em JavaScript, verifique se o componente foi projetado para obter bom desempenho. Sua superfície de API define o limite de interoperabilidade e o quanto os usuários precisarão pensar sobre as orientações neste tópico. Esse fator é especialmente importante se você está distribuindo seus componentes a terceiros.

Todas as sugestões com vistas ao bom desempenho nos aplicativos são extensivas aos componentes. Avalie seu componente para observar quais APIs têm altos padrões de tráfego e, nessas áreas, considere o fornecimento de APIs que permitam aos seus usuários trabalhar com poucas chamadas. Empenhamos esforços significativos durante o desenvolvimento do Windows Runtime para permitir que os aplicativos o utilizem sem precisar transpor o limite da interoperabilidade com frequência.