Überlegungen zur Leistung von Interop (C++)
Dieses Thema enthält Richtlinien zum Verringern der Auswirkungen von verwalteten/nicht verwalteten Interopübergängen auf die Laufzeitleistung.
Visual C++ unterstützt dieselben Interoperabilitätsmechanismen wie andere .NET-Sprachen wie Visual Basic und C# (P/Invoke), bietet aber auch Interopunterstützung, die für Visual C++ (C++-Interoperabilität) spezifisch ist. Für leistungskritische Anwendungen ist es wichtig, die Leistungsauswirkungen der einzelnen Interoptechniken zu verstehen.
Unabhängig von der verwendeten Interoptechnik sind spezielle Übergangssequenzen, die als Thunks bezeichnet werden, jedes Mal erforderlich, wenn eine verwaltete Funktion eine nicht verwaltete Funktion aufruft und umgekehrt. Diese Thunks werden automatisch vom Microsoft C++-Compiler eingefügt, aber es ist wichtig zu beachten, dass diese Übergänge kumulativ in Bezug auf die Leistung teuer sein können.
Reduzieren von Übergängen
Eine Möglichkeit, die Kosten von Interop-Thunks zu vermeiden oder zu reduzieren, besteht darin, die schnittstellen zu umgestalten, um verwaltete/nicht verwaltete Übergänge zu minimieren. Dramatische Leistungsverbesserungen können durch die Ausrichtung auf Chatty-Schnittstellen vorgenommen werden, bei denen es sich um häufig verwendete Anrufe über die verwaltete/nicht verwaltete Grenze handelt. Eine verwaltete Funktion, die eine nicht verwaltete Funktion in einer engen Schleife aufruft, ist z. B. ein guter Kandidat für die Umgestaltung. Wenn die Schleife selbst auf die nicht verwaltete Seite verschoben wird oder eine verwaltete Alternative zum nicht verwalteten Aufruf erstellt wird (z. B. Daten auf der verwalteten Seite warteschlangen und dann alle gleichzeitig an die nicht verwaltete API nach der Schleife marshallen), kann die Anzahl der Übergänge erheblich reduziert werden.
P/Invoke vs. C++-Interoperabilität
Für .NET-Sprachen, z. B. Visual Basic und C#, ist die vorgeschriebene Methode für die Interoperabilität mit systemeigenen Komponenten P/Invoke. Da P/Invoke von .NET Framework unterstützt wird, unterstützt Visual C++ sie auch, aber Visual C++ bietet auch eine eigene Interoperabilitätsunterstützung, die als C++ Interop bezeichnet wird. C++-Interoperabilität wird gegenüber P/Invoke bevorzugt, da P/Invoke nicht typsicher ist. Daher werden Fehler in erster Linie zur Laufzeit gemeldet, aber C++-Interop hat auch Leistungsvorteile gegenüber P/Invoke.
Beide Techniken erfordern mehrere Dinge, wenn eine verwaltete Funktion eine nicht verwaltete Funktion aufruft:
Die Funktionsaufrufargumente werden von CLR in systemeigene Typen gemarstet.
Eine verwaltete zu verwaltende Thunk wird ausgeführt.
Die nicht verwaltete Funktion wird aufgerufen (mit den systemeigenen Versionen der Argumente).
Ein nicht verwaltetes Thunk wird ausgeführt.
Der Rückgabetyp und alle Argumente "out" oder "in"out" werden von nativen zu CLR-Typen gemarstet.
Die verwalteten/nicht verwalteten Thunks sind für die Interoperabilität überhaupt erforderlich, aber die erforderliche Datenmarsing hängt von den beteiligten Datentypen, der Funktionssignatur und der Verwendung der Daten ab.
Die von C++ Interop durchgeführte Datenmarsing ist die einfachste Form: Die Parameter werden einfach über die verwaltete/nicht verwaltete Grenze in bitweise kopiert; es wird überhaupt keine Transformation ausgeführt. Bei P/Invoke gilt dies nur, wenn alle Parameter einfach, blittable Typen sind. Andernfalls führt P/Invoke sehr robuste Schritte aus, um jeden verwalteten Parameter in einen entsprechenden systemeigenen Typ zu konvertieren, und umgekehrt, wenn die Argumente als "out" oder "in,out" gekennzeichnet sind.
Mit anderen Worten: C++ Interop verwendet die schnellstmögliche Methode des Datenmarsings, während P/Invoke die robusteste Methode verwendet. Dies bedeutet, dass C++-Interoperabilität (in einer für C++typischen Mode) standardmäßig eine optimale Leistung bietet, und der Programmierer ist für die Behandlung von Fällen verantwortlich, in denen dieses Verhalten nicht sicher oder angemessen ist.
C++ Interop erfordert daher, dass Datenmarsing explizit bereitgestellt werden muss, aber der Vorteil besteht darin, dass der Programmierer frei ist zu entscheiden, was angemessen ist, angesichts der Art der Daten und deren Verwendung. Darüber hinaus kann das Verhalten des P/Invoke-Datenmarsings in einem Grad angepasst werden, C++ Interop ermöglicht die Anpassung von Datenmarsing auf Aufrufbasis. Dies ist mit P/Invoke nicht möglich.
Weitere Informationen zu C++-Interop finden Sie unter Verwenden von C++-Interop (impliziter PInvoke).