2009-10-23 15 views
10

Tiene que haber una manera mejor y más rápido para intercambiar bytes de palabras de 16 bits, entonces esto .:manera más rápida de cambiar orden de bits en C# con palabras de 16 bits

public static void Swap(byte[] data) 
{ 
    for (int i = 0; i < data.Length; i += 2) 
    { 
     byte b = data[i]; 
     data[i] = data[i + 1]; 
     data[i + 1] = b; 
    } 
} 

¿Alguien tiene una idea?

+2

Su solución parece una muy buena, excepto que SI SUS DATOS TIENEN LÍNEAS EXTRAORDINARIAS, su código arrojará una matriz fuera de la excepción encuadernada. – NawaMan

+1

Si está intercambiando palabras de 16 bits, entonces sus datos nunca tendrán una longitud impar. –

+0

Sí, este será un método privado y se garantizará que tenga palabras de 16 bits. – initialZero

Respuesta

7

En mi intento de solicitar el premio Uberhacker, presento lo siguiente. Para mis pruebas, he usado un array Fuente de 8.192 bytes y llamé SwapX2 100.000 veces:

public static unsafe void SwapX2(Byte[] source) 
{ 
    fixed (Byte* pSource = &source[0]) 
    { 
     Byte* bp = pSource; 
     Byte* bp_stop = bp + source.Length; 

     while (bp < bp_stop) 
     { 
      *(UInt16*)bp = (UInt16)(*bp << 8 | *(bp + 1)); 
      bp += 2; 
     } 
    } 
} 

Mi evaluación comparativa indica que esta versión es más de 1,8 veces más rápido que el código presentado en la pregunta original.

1

Bueno, podría usar el XOR swapping trick, para evitar un byte intermedio. No será más rápido, sin embargo, y no me sorprendería si el IL es exactamente el mismo.

for (int i = 0; i < data.Length; i += 2) 
{ 
    data[i] ^= data[i + 1]; 
    data[i + 1] ^= data[i]; 
    data[i] ^= data[i + 1]; 
} 
+1

Bueno uno. Pero esto es algo más confuso de leer. Y wikipedia afirma que "en las CPU modernas (de escritorio), la técnica XOR es considerablemente más lenta que usar una variable temporal para hacer el intercambio". Oh bien. Gracias por la solución sexy. – initialZero

+0

Sí, creo que todo lo que podemos decir es que está limpio. Acabo de compararlo y obtuve resultados idénticos a su solución. Entonces, una de las formas probablemente se optimiza para la otra. –

6

De esta manera parece ser ligeramente más rápido que el método de la pregunta original:

private static byte[] _temp = new byte[0]; 
public static void Swap(byte[] data) 
{ 
    if (data.Length > _temp.Length) 
    { 
     _temp = new byte[data.Length]; 
    } 
    Buffer.BlockCopy(data, 1, _temp, 0, data.Length - 1); 
    for (int i = 0; i < data.Length; i += 2) 
    { 
     _temp[i + 1] = data[i]; 
    } 
    Buffer.BlockCopy(_temp, 0, data, 0, data.Length); 
} 

Mi evaluación comparativa supone que el método es llamado en varias ocasiones, por lo que el cambio de tamaño de la matriz no es _temp un factor. Este método se basa en el hecho de que la mitad del intercambio de bytes se puede hacer con la llamada inicial Buffer.BlockCopy(...) (con la posición de origen compensada por 1).

Por favor comparen esto, en caso de que haya perdido completamente la razón. En mis pruebas, este método toma aproximadamente el 70% del tiempo que el método original (que modifiqué para declarar el byte b fuera del ciclo).

+0

¡Premio Uberhacker! Esperaría que un gran cambio de tamaño de la temperatura sea más caro que un intercambio en el lugar. Y es solo una pérdida de memoria al tener la temperatura sentada. – initialZero

+1

@initialZero: en mi punto de referencia, inicialmente lo dimensioné con el mismo tamaño que mi matriz de prueba, por lo que no hubo ningún costo allí. Las variables de temperatura como esta en general compran velocidad a expensas de la memoria, lo que puede o no ser una buena decisión. – MusiGenesis

+0

@MusiGenesis siempre puede establecer temp = null al final del intercambio para corregir la fuga. Definitivamente es una buena solución. Gracias. – initialZero

4

siempre me gusta esto:

public static Int64 SwapByteOrder(Int64 value) 
{ 
    var uvalue = (UInt64)value; 
    UInt64 swapped = 
     ((0x00000000000000FF) & (uvalue >> 56) 
     | (0x000000000000FF00) & (uvalue >> 40) 
     | (0x0000000000FF0000) & (uvalue >> 24) 
     | (0x00000000FF000000) & (uvalue >> 8) 
     | (0x000000FF00000000) & (uvalue << 8) 
     | (0x0000FF0000000000) & (uvalue << 24) 
     | (0x00FF000000000000) & (uvalue << 40) 
     | (0xFF00000000000000) & (uvalue << 56)); 
    return (Int64)swapped; 
} 

Creo que usted encontrará que este es el método más rápido y también un ser bastante legible y segura. Obviamente, esto se aplica a los valores de 64 bits, pero la misma técnica podría usarse para 32 o 16.

Cuestiones relacionadas