2009-08-31 11 views
5

Ho una matrice di tipi uint in C#, Dopo aver controllato se il programma sta lavorando su una macchina little-endian, voglio convertire i dati in un big-endian genere. Poiché la quantità di dati può diventare molto grande ma è sempre uniforme, stavo pensando di considerare due tipi di uint come un tipo ulong, per una performance migliore e programmarlo in ASM, quindi sto cercando un modo molto veloce (il più veloce possibile) Algoritmo di assembler per convertire little-endian in big-endian.Conversione da little-endian a big-endian in ASM

+0

definire per me "molto grande"? –

+0

almeno 1'000'000 uints (forse un po 'meno ma può diventare più grande) –

risposta

6

Per una grande quantità di dati, l'istruzione bswap (disponibile in Visual C++ sotto gli intrinseci _byteswap_ushort, _byteswap_ulong, and _byteswap_uint64) è la strada da percorrere. Questo supererà anche l'assemblaggio scritto a mano. Queste non sono disponibili in puro C# senza P/Invoke, in modo da:

  1. utilizzarla solo se si dispone di un sacco di dati scambio di byte.
  2. Si dovrebbe seriamente considerare di scrivere l'I/O dell'applicazione di livello più basso nel C++ gestito in modo da poter eseguire lo scambio prima di portare i dati in un array gestito. Dovete già scrivere una libreria C++, quindi non c'è molto da perdere e voi eludete tutti i problemi di prestazioni relativi a P/Invoke per algoritmi a bassa complessità che operano su dataset di grandi dimensioni.

PS: Molte persone non sono consapevoli dell'intreccio di byte swap. La loro performance è sorprendente, doppiamente così per i dati in virgola mobile perché li elabora come interi. Non c'è modo di batterlo senza codificare a mano i carichi del tuo registro per ogni singolo caso di scambio di byte, e se dovessi provare, probabilmente otterrai un maggior successo nell'ottimizzatore di quanto tu non abbia mai capito.

1

stavo pensando di prendere in considerazione due tipi uint come un tipo ulong

Beh, che sarebbe anche scambiare i due valori uint, che potrebbe non essere desiderabile ...

Si potrebbe prova un codice C# in modalità non sicura, che potrebbe effettivamente funzionare abbastanza bene. Come:

public static unsafe void SwapInts(uint[] data) { 
    int cnt = data.Length; 
    fixed (uint* d = data) { 
     byte* p = (byte*)d; 
     while (cnt-- > 0) { 
     byte a = *p; 
     p++; 
     byte b = *p; 
     *p = *(p + 1); 
     p++; 
     *p = b; 
     p++; 
     *(p - 3) = *p; 
     *p = a; 
     p++; 
     } 
    } 
} 

Sul mio computer il throughput è di circa 2 GB al secondo.

2

Si consiglia di ripensare semplicemente il problema, questo non dovrebbe essere un collo di bottiglia. Prendi l'algoritmo ingenuo (scritto in assembly CLI, solo per divertimento). Assumiamo il numero che vogliamo è in numero locale 0

LDLOC 0 
SHL 24 
LDLOC 0 
LDC.i4 0x0000ff00 
SHL 8 
OR 
LDLOC 0 
LDC.i4 0x00ff0000 
SHL.UN 8 
OR 
LDLOC 0 
SHL.UN 24 
OR 

Al massimo che è 13 istruzioni (x86) di assemblaggio per il numero (e molto probabilmente l'interprete sarà ancora più intelligente utilizzando registri intelligenti). E non diventa più ingenuo di così.

Ora, si confronti con i costi di

  • ottenere i dati caricati in (tra cui qualunque periferiche si sta lavorando!)
  • Maniuplation dei dati (fare confronti, per esempio)
  • Emissione del risultato (qualunque esso sia)

Se 13 di istruzioni al numero è un pezzo significativo del tempo di esecuzione, allora si sta facendo un compito MOLTO ad alte prestazioni e dovrebbe avere il tuo input nel formato corretto! Probabilmente non useresti un linguaggio gestito perché vorresti avere un controllo molto maggiore su buffer di dati e quant'altro, e nessun controllo di limiti di array aggiuntivi.

Se tale matrice di dati attraversa una rete, mi aspetto che ci siano costi molto maggiori dalla gestione dei socket che da un semplice capovolgimento dell'ordine dei byte, se si tratta di disco, considerare il pre-flipping prima di eseguire questo programma.