Considérations sur les performances de l'interopérabilité (C++)
Cette rubrique fournit des instructions pour réduire l’effet des transitions d’interopérabilité managées/non managées sur les performances d’exécution.
Visual C++ prend en charge les mêmes mécanismes d’interopérabilité que d’autres langages .NET tels que Visual Basic et C# (P/Invoke), mais il fournit également la prise en charge de l’interopérabilité spécifique à Visual C++ (interopérabilité C++). Pour les applications critiques en termes de performances, il est important de comprendre les implications en termes de performances de chaque technique d’interopérabilité.
Quelle que soit la technique d’interopérabilité utilisée, les séquences de transition spéciales, appelées thunks, sont requises chaque fois qu’une fonction managée appelle une fonction non managée et vice versa. Ces thunks sont insérés automatiquement par le compilateur Microsoft C++, mais il est important de garder à l’esprit que cumulativement, ces transitions peuvent être coûteuses en termes de performances.
Réduction des transitions
Une façon d’éviter ou de réduire le coût des segments d’interopérabilité consiste à refactoriser les interfaces impliquées pour réduire les transitions managées/non managées. Des améliorations spectaculaires des performances peuvent être apportées en ciblant les interfaces chatty, qui sont celles qui impliquent des appels fréquents sur la limite managée/non managée. Une fonction managée qui appelle une fonction non managée dans une boucle serrée, par exemple, est un bon candidat pour la refactorisation. Si la boucle elle-même est déplacée vers le côté non managé ou si une alternative managée à l’appel non managé est créée (peut-être mettre en file d’attente des données côté managé, puis la marshaler sur l’API non managée une fois après la boucle), le nombre de transitions peut être réduit de manière significative.
P/Invoke vs. Interopérabilité C++
Pour les langages .NET, tels que Visual Basic et C#, la méthode prescrite pour l’interopérabilité avec les composants natifs est P/Invoke. Étant donné que P/Invoke est pris en charge par .NET Framework, Visual C++ le prend également en charge, mais Visual C++ fournit également sa propre prise en charge de l’interopérabilité, appelée Interop C++. L’interopérabilité C++ est préférée à P/Invoke, car P/Invoke n’est pas de type sécurisé. Par conséquent, les erreurs sont principalement signalées au moment de l’exécution, mais L’interopérabilité C++ présente également des avantages en matière de performances par rapport à P/Invoke.
Les deux techniques nécessitent plusieurs choses à chaque fois qu’une fonction managée appelle une fonction non managée :
Les arguments d’appel de fonction sont marshalés de CLR vers des types natifs.
Un thunk managé à non managé est exécuté.
La fonction non managée est appelée (à l’aide des versions natives des arguments).
Un thunk non managé est exécuté.
Le type de retour et tous les arguments « out » ou « in , out » sont marshalés de type natif vers les types CLR.
Les thunks managés/non managés sont nécessaires pour que l’interopérabilité fonctionne du tout, mais le marshaling de données requis dépend des types de données impliqués, de la signature de fonction et de la façon dont les données seront utilisées.
Le marshaling de données effectué par L’interopérabilité C++ est la forme la plus simple possible : les paramètres sont simplement copiés sur la limite managée/non managée de manière bit ; aucune transformation n’est effectuée du tout. Pour P/Invoke, cela n’est vrai que si tous les paramètres sont des types blittables simples. Sinon, P/Invoke effectue des étapes très robustes pour convertir chaque paramètre managé en type natif approprié, et vice versa si les arguments sont marqués comme « out » ou « in,out ».
En d’autres termes, C++ Interop utilise la méthode la plus rapide possible de marshaling de données, tandis que P/Invoke utilise la méthode la plus robuste. Cela signifie que l’interopérabilité C++ (d’une manière typique pour C++) offre des performances optimales par défaut, et le programmeur est chargé de traiter les cas où ce comportement n’est pas sûr ou approprié.
L’interopérabilité C++ nécessite donc que le marshaling de données soit fourni explicitement, mais l’avantage est que le programmeur est libre de décider ce qui convient, en fonction de la nature des données et de la façon dont il doit être utilisé. En outre, bien que le comportement du marshaling de données P/Invoke puisse être modifié à un degré personnalisé, L’interopérabilité C++ permet au marshaling de données d’être personnalisé en fonction de l’appel. Cela n’est pas possible avec P/Invoke.
Pour plus d’informations sur l’interopérabilité C++, consultez Utilisation de l’interopérabilité C++ (PInvoke implicite).