Considerazioni sulle prestazioni per l'interoperabilità (C++)

In questo argomento vengono fornite linee guida per ridurre l'effetto delle transizioni di interoperabilità gestite/non gestite sulle prestazioni in fase di esecuzione.

Visual C++ supporta gli stessi meccanismi di interoperabilità di altri linguaggi .NET, ad esempio Visual Basic e C# (P/Invoke), ma offre anche il supporto di interoperabilità specifico per Visual C++ (interoperabilità C++). Per le applicazioni critiche per le prestazioni, è importante comprendere le implicazioni sulle prestazioni di ogni tecnica di interoperabilità.

Indipendentemente dalla tecnica di interoperabilità usata, le sequenze di transizione speciali, denominate batchk, sono necessarie ogni volta che una funzione gestita chiama una funzione non gestita e viceversa. Questi batchk vengono inseriti automaticamente dal compilatore Microsoft C++, ma è importante tenere presente che cumulativamente, queste transizioni possono essere costose in termini di prestazioni.

Riduzione delle transizioni

Un modo per evitare o ridurre il costo dei batch di interoperabilità consiste nel effettuare il refactoring delle interfacce coinvolte per ridurre al minimo le transizioni gestite/non gestite. I miglioramenti drammatici delle prestazioni possono essere apportati tramite la destinazione di interfacce di chatty, che sono quelle che hanno coinvolto chiamate frequenti attraverso il limite gestito/non gestito. Una funzione gestita che chiama una funzione non gestita in un ciclo stretto, ad esempio, è un buon candidato per il refactoring. Se il ciclo stesso viene spostato sul lato non gestito o se viene creata un'alternativa gestita alla chiamata non gestita (ad esempio accodare i dati sul lato gestito e quindi effettuarne il marshalling all'API non gestita contemporaneamente dopo il ciclo), il numero di transizioni può essere ridotto in modo significativo.

Interoperabilità P/Invoke e C++

Per i linguaggi .NET, ad esempio Visual Basic e C#, il metodo prescritto per l'interoperabilità con i componenti nativi è P/Invoke. Poiché P/Invoke è supportato da .NET Framework, Visual C++ lo supporta anche, ma Visual C++ offre anche il proprio supporto per l'interoperabilità, detto interoperabilità C++. L'interoperabilità C++ è preferibile rispetto a P/Invoke perché P/Invoke non è indipendente dai tipi. Di conseguenza, gli errori vengono segnalati principalmente in fase di esecuzione, ma l'interoperabilità C++ presenta anche vantaggi in termini di prestazioni rispetto a P/Invoke.

Entrambe le tecniche richiedono che vengano eseguite diverse operazioni ogni volta che una funzione gestita chiama una funzione non gestita:

  • Gli argomenti di chiamata di funzione vengono sottoposto a marshalling da CLR a tipi nativi.

  • Viene eseguito un threadk gestito a non gestito.

  • La funzione non gestita viene chiamata (usando le versioni native degli argomenti).

  • Viene eseguito un threadk da non gestito a gestito.

  • Il tipo restituito e gli argomenti "out" o "in,out" vengono sottoposto a marshalling dai tipi nativi a CLR.

I kubernetes gestiti/non gestiti sono necessari per il funzionamento dell'interoperabilità, ma il marshalling dei dati necessario dipende dai tipi di dati coinvolti, dalla firma della funzione e dal modo in cui verranno usati i dati.

Il marshalling dei dati eseguito dall'interoperabilità C++ è il formato più semplice possibile: i parametri vengono semplicemente copiati attraverso il limite gestito/non gestito in modo bit per bit; non viene eseguita alcuna trasformazione. Per P/Invoke, questo vale solo se tutti i parametri sono tipi semplici e copiabili da blt. In caso contrario, P/Invoke esegue passaggi molto affidabili per convertire ogni parametro gestito in un tipo nativo appropriato e viceversa se gli argomenti sono contrassegnati come "out" o "in,out".

In altre parole, l'interoperabilità C++ usa il metodo di marshalling dei dati più veloce possibile, mentre P/Invoke usa il metodo più affidabile. Ciò significa che l'interoperabilità C++ (in modo tipico per C++) offre prestazioni ottimali per impostazione predefinita e il programmatore è responsabile di risolvere i casi in cui questo comportamento non è sicuro o appropriato.

L'interoperabilità C++ richiede pertanto che il marshalling dei dati debba essere fornito in modo esplicito, ma il vantaggio è che il programmatore è libero di decidere cosa sia appropriato, data la natura dei dati e come usarli. Inoltre, anche se il comportamento del marshalling dei dati P/Invoke può essere modificato in base alla personalizzazione, l'interoperabilità C++ consente di personalizzare il marshalling dei dati in base a una chiamata. Questo non è possibile con P/Invoke.

Per altre informazioni sull'interoperabilità C++, vedere Uso dell'interoperabilità C++ (PInvoke implicito).

Vedi anche

Assembly misti (nativi e gestiti)