Partilhar via


Considerações sobre desempenho para interop (C++)

Este tópico fornece diretrizes para reduzir o efeito das transições de interoperabilidade gerenciadas/não gerenciadas no desempenho em tempo de execução.

O Visual C++ dá suporte aos mesmos mecanismos de interoperabilidade que outras linguagens .NET, como Visual Basic e C# (P/Invoke), mas também fornece suporte a interoperabilidade específico ao Visual C++ (Interoperabilidade C++). Para aplicativos em que o desempenho é crítico, é importante entender as implicações de desempenho de cada técnica de interoperabilidade.

Independentemente da técnica de interoperabilidade usada, sequências de transição especiais, chamadas de thunks, são necessárias sempre que uma função gerenciada chama uma função não gerenciada e vice-versa. Esses thunks são inseridos automaticamente pelo compilador do Microsoft C++, mas é importante ter em mente que, cumulativamente, essas transições podem ser onerosas em termos de desempenho.

Reduzindo transições

Uma maneira de evitar ou reduzir o custo de thunks de interoperabilidade é refatorar as interfaces envolvidas para minimizar as transições gerenciadas/não gerenciadas. Aprimoramentos dramáticos de desempenho podem ser feitos direcionando esforços a interfaces verborrágicas, que são aquelas que envolveram chamadas frequentes no limite gerenciado/não gerenciado. Uma função gerenciada que chama uma função não gerenciada em um loop estreito, por exemplo, é uma boa candidata à refatoração. Se o loop em si for movido para o lado não gerenciado ou se uma alternativa gerenciada à chamada não gerenciada for criada (talvez enfileirando dados no lado gerenciado e, em seguida, fazendo marshaling para a API não gerenciada de uma só vez após o loop), o número de transições poderá ser reduzido significativamente.

Interoperabilidade P/Invoke vs. C++

Para linguagens .NET, como Visual Basic e C#, o método prescrito para interoperação com componentes nativos é o P/Invoke. Como o P/Invoke é compatível com o .NET Framework, o Visual C++ também dá suporte a ele, mas o Visual C++ também fornece um suporte próprio à interoperabilidade, que é conhecido como Interoperabilidade C++. A Interoperabilidade C++ é preferível ao P/Invoke porque o P/Invoke não fortemente tipado. Como resultado, os erros são relatados principalmente em tempo de execução, mas a Interoperabilidade C++ também tem vantagens de desempenho sobre o P/Invoke.

Ambas as técnicas exigem que várias coisas aconteçam sempre que uma função gerenciada chama uma função não gerenciada:

  • Os argumentos de chamada de função são empacotados de CLR para tipos nativos.

  • Um thunk gerenciado para não gerenciado é executado.

  • A função não gerenciada é chamada (usando as versões nativas dos argumentos).

  • Um thunk não gerenciado para gerenciado é executado.

  • O tipo de retorno e quaisquer argumentos "out" ou "in,out" são submetidos a marshaling de tipos nativos para CLR.

Os thunks gerenciados/não gerenciados são necessários para que a interoperabilidade funcione, mas o marshaling de dados necessário depende dos tipos de dados envolvidos, da assinatura da função e de como os dados serão usados.

O marshaling de dados executado pela Interoperabilidade C++ é a forma mais simples possível: os parâmetros são simplesmente copiados no limite gerenciado/não gerenciado bit a bit; nenhuma transformação é executada. Para P/Invoke, isso só será verdadeiro se todos os parâmetros forem tipos simples e blittable. Caso contrário, o P/Invoke executará etapas muito robustas para converter cada parâmetro gerenciado em um tipo nativo apropriado e vice-versa, se os argumentos forem marcados como "out" ou "in,out".

Em outras palavras, o C++ Interop usa o método mais rápido possível de marshaling de dados, enquanto o P/Invoke usa o método mais robusto. Isso significa que a Interoperabilidade C++ (de maneira típica para C++) fornece o desempenho ideal por padrão, e o programador é responsável por resolver casos em que esse comportamento não é seguro ou apropriado.

A Interoperabilidade C++ exige, portanto, que o marshaling de dados seja fornecido explicitamente, mas a vantagem é que o programador tem liberdade para decidir o que é apropriado, dada a natureza dos dados e como eles devem ser usados. Além disso, embora o comportamento do marshaling de dados P/Invoke possa ser modificado em um grau personalizado, a Interoperabilidade C++ permite que o marshaling de dados seja personalizado para cada chamada. Isso não é possível com o P/Invoke.

Para obter mais informações sobre a Interoperabilidade C++, confira Usando a Interoperabilidade C++ (PInvoke Implícito).

Confira também

Assemblies mistos (nativos e gerenciados)