Domanda

Ho una firma del metodo C ++ che assomiglia a questa:

    static extern void ImageProcessing(
        [MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
        [MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
        int inYSize, int inXSize);

Ho avvolto la funzione in metodi di temporizzazione, sia interni che esterni. Internamente, la funzione è in esecuzione a 0,24 secondi. Esternamente, la funzione viene eseguita in 2,8 secondi, o circa 12 volte più lentamente. Cosa sta succedendo? Il marshalling mi sta rallentando così tanto? Se lo è, come posso aggirarlo? Devo andare a un codice non sicuro e utilizzare i puntatori o qualcosa del genere? Sono un po 'confuso da dove viene il costo del tempo extra.

È stato utile?

Soluzione 3

La risposta è, purtroppo, molto più banale di questi suggerimenti, sebbene aiutino. Fondamentalmente, ho incasinato il modo in cui stavo facendo i tempi.

Il codice di temporizzazione che stavo usando era questo:

Ipp32s timer;
ippGetCpuFreqMhz(&timer);
Ipp64u globalStart = ippGetCpuClocks();
globalStart = ippGetCpuClocks() *2 - globalStart; //use this method to get rid of the overhead of getting clock ticks

      //do some stuff

Ipp64u globalEnd = ippGetCpuClocks(); 
globalEnd = ippGetCpuClocks() *2 - globalEnd;
std::cout << "total runtime: " << ((Ipp64f)globalEnd - (Ipp64f)globalStart)/((Ipp64f)timer *1000000.0f) << " seconds" << std::endl;

Questo codice è specifico per il compilatore Intel ed è progettato per fornire misurazioni del tempo estremamente precise. Sfortunatamente, quell'estrema precisione comporta un costo di circa 2,5 secondi per corsa. La rimozione del codice di temporizzazione ha rimosso tale vincolo temporale.

Tuttavia, sembra esserci ancora un ritardo del tempo di esecuzione: il codice segnalerebbe 0,24 s con quel codice di temporizzazione attivo e ora sta riportando un tempo di circa 0,35 secondi, il che significa che c'è un costo della velocità di circa il 50%.

Modifica del codice in questo:

  static extern void ImageProcessing(
     IntPtr inImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
     IntPtr outImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
     int inYSize, int inXSize);

e chiamato come:

        unsafe {
            fixed (ushort* inImagePtr = theInputImage.DataArray){
                fixed (ushort* outImagePtr = theResult){
                    ImageProcessing((IntPtr)inImagePtr,//theInputImage.DataArray,
                        (IntPtr)outImagePtr,//theResult,
                        ysize,
                        xsize);
                }
            }
        }

riduce il tempo dell'eseguibile a 0,3 s (media di tre esecuzioni). Ancora troppo lento per i miei gusti, ma un miglioramento della velocità di 10 volte rientra sicuramente nel campo dell'accettabilità per il mio capo.

Altri suggerimenti

Dai un'occhiata a questo articolo . Mentre il focus è sul Compact Framework, i principi generali si applicano anche al desktop. Una citazione pertinente dalla sezione di analisi è la seguente:

  

La chiamata gestita non chiama direttamente il metodo nativo. Invece chiama un metodo stub JITted che deve eseguire alcune routine generali come le chiamate per determinare lo stato Preemption GC (per determinare se un GC è in sospeso e dobbiamo attendere). È anche possibile che anche alcuni codici di marshalling vengano inseriti nello stub JIT. Tutto ciò richiede tempo.

Modifica : vale la pena leggere anche questo articolo del blog su perf di codice JITted - di nuovo, specifico per CF, ma ancora rilevante. C'è anche un articolo che illustra la profondità dello stack di chiamate e il suo impatto su perf , sebbene questo sia probabilmente specifico per CF (non testato sul desktop).

Hai provato a cambiare i due parametri dell'array in IntPtr? PInvoke è al massimo assoluto quando tutti i tipi nella firma di marshalling sono disponibili. Ciò significa che Pinvoke si riduce a un semplice memcpy per ottenere i dati avanti e indietro.

Nel mio team abbiamo trovato il modo più efficace per gestire il nostro livello PInvoke è

  1. Garantire che tutto ciò che è Marshall sia riproducibile
  2. Paga il prezzo manualmente ai tipi di Maresciallo come gli array manipolando una classe IntPtr in base alle necessità. Questo è molto banale in quanto abbiamo molti metodi / classi wrapper.

Come con qualsiasi " questo sarà più veloce " risposta, dovrai profilare questa è la tua base di codice. Siamo arrivati ??a questa soluzione solo dopo aver considerato e profilato diversi metodi.

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