Domanda

Nel processo di apprendimento di P / Invoke, ho posto questa domanda precedente:

  

Come P / Invocare quando sono coinvolti puntatori

Tuttavia, non capisco bene le implicazioni dell'uso di P / Invoke in C # rispetto alla creazione di un wrapper in Managed C ++. La creazione della stessa DLL usando P / Invoke in C # ha sicuramente prodotto un'interfaccia più pulita poiché potrei usare DLLImport su una risorsa integrata, ma un wrapper C ++ gestito per una DLL nativa, dove eseguo il marshalling da solo, avrebbe prestazioni migliori?

È stato utile?

Soluzione

Il wrapper C ++ dovrebbe essere più veloce, dai un'occhiata a questa pagina MSDN :

  

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

Quindi sostanzialmente il motivo principale è che P / Invoke esegue il pinning, il blitting, il controllo degli errori, mentre l'interoperabilità C ++ spinge semplicemente i parametri nello stack e chiama la funzione.

Un altro punto da ricordare è che C ++ può chiamare diverse API in una singola chiamata mentre P / Invoke OGNI parametro passato dall'indirizzo viene bloccato e sbloccato su OGNI chiamata, copiato e copiato indietro, ecc.

Altri suggerimenti

Otterresti prestazioni migliori? Dipende da cosa stai facendo e da come lo stai facendo. In generale, il tuo colpo di performance verrà più probabilmente dal fare transizioni gestite / non gestite e più di quelle che puoi tagliare, meglio è. Idealmente, l'interfaccia con il codice non gestito dovrebbe essere pesante e non loquace.

Supponiamo che tu abbia un codice non gestito che ha una raccolta di alcune migliaia di oggetti. È possibile esporre un'API come questa al codice gestito:

int GetFooCount();
IntPtr GetFoo(int n);
void ReleaseFoo(IntPtr p);

e va bene, fino a quando non inizi a usarlo in C # in questo modo:

int total = API.GetFooCount();
IntPtr[] objects = new IntPtr[total];
for (int i=0; i < total; i++) {
    objects[i] = GetFoo(i);
}
// and later:
foreach (IntPtr p in objects) { ReleaseFoo(p); }

che per un totale == 1000, saranno 4002 transizioni gestite / non gestite. Se invece hai questo:

int GetFooCount();
void GetFoos(IntPtr[] arr, int start, int count);
void ReleaseFoos(IntPtr arr, int start, int count);

quindi puoi fare lo stesso lavoro con 6 transizioni. Quale pensi che funzionerà meglio?

Ovviamente, la prossima importante domanda da porsi è " vale la pena guadagnare questo rendimento? " quindi ricordati di misurare prima.

Una cosa di cui dovresti essere consapevole è che possono accadere cose divertenti a STL quando lavori con C ++ gestito. Ho un codice di libreria non gestito che utilizza STL. La mia esperienza è stata che se avessi mai toccato uno dei tipi di STL in C ++ gestito, TUTTI sarebbero diventati implementazioni gestite. Il risultato finale di questo è stato che il codice di basso livello stava facendo transizioni gestite / non gestite mentre ripeteva gli elenchi. Yikes. Ho risolto questo problema non esponendo mai i tipi STL al C ++ gestito.

Nella nostra esperienza, è molto meglio (se possibile) passare alla libreria statica C # - > managed C ++ wrapper- > se hai la possibilità di farlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top