10
votes

Manière plus rapide d'échanger de l'endanse en C # avec des mots de 16 bits

Il faut que ce soit un moyen plus rapide et plus préférable d'échanger des octets de mots 16 bits alors cela.:

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;
    }
}


7 commentaires

Votre solution ressemble à une bonne chose, sauf que si vos données sont toujours longues, votre code jettera un tableau hors de l'exception liée.


S'il échange des mots 16 bits, ses données n'auront jamais de longueur étrange.


Oui, ce sera une méthode privée et il sera garanti d'avoir des mots 16 bits.


Pourquoi cherchez-vous plus vite et mieux? Y a-t-il une métrique que vous visez?


Ceci est une solution O (n), sans nouvelles allocations de mémoire. Autre puis déclarant B à l'extérieur de la boucle afin que cela ne soit pas alloué à chaque fois, pourquoi voulez-vous améliorer cela?


@initialzero, la seule chose à laquelle je puisse penser à l'amélioration de cela, réduit la réduction des opcodes sur un niveau de msil à l'intérieur de la boucle. Mais vous devez parcourir la totalité de la matrice. Une autre approche serait de déclarer votre propre type de tableau d'octets, qui peut renvoyer à la fois des petits et grands Endian en remplaçant l'opérateur []. De cette façon, vous n'aurez pas besoin d'échanger la mémoire.


Je pense que les changements de bits rendraient cela plus vite, mais FWIW, je suis d'accord que vous ne devriez pas vérifier les tableaux de longueur même. À ce niveau dans un cadre de méthodes privées, il est plus que raisonnable de rendre cette hypothèse et de compter sur l'exception hors de portée.


5 Réponses :


1
votes

Eh bien, vous pouvez utiliser le XOR Swapping Trick , pour éviter un octet intermédiaire. Ce ne sera pas plus rapide, cependant, et je ne serais pas surpris si l'IL est exactement la même.

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


2 commentaires

Bonne. Mais c'est un peu plus déroutant de lire. Et Wikipedia affirme "sur des processeurs modernes (de bureau), la technique XOR est considérablement plus lente que d'utiliser une variable temporaire pour échanger." Tant pis. Merci pour la solution sexy.


Ouais, je pense que tout ce que nous pouvons dire à ce sujet est que c'est soigné. Je viens de le comparer et j'ai reçu des résultats identiques à votre solution. Ainsi, l'une des formes est probablement optimisée à l'autre.



6
votes

De cette façon semble être légèrement plus rapide que la méthode de la question originale:

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);
}


5 commentaires

Award Uberhacker! Je m'attendrais à ce qu'un grand redimensionnement de Temp soit plus cher que d'un échange en place. Et c'est un peu une fuite de mémoire juste avoir la tempête de s'asseoir.


@initialzero: Dans mon repère, je l'ai initialement dimensionné à la même taille que mon tableau de test, il n'y avait donc aucun coût là-bas. Les variables temporaires telles que celle-ci en général Achetez la vitesse au détriment de la mémoire, qui peut ou non être une bonne décision.


@Musigenesis Vous pouvez toujours définir Temp = NULL à la fin de l'échange pour réparer la fuite. C'est définitivement une solution cool. Merci.


@Fengyuan: toutes les méthodes ne doivent pas être thread-coffre-fort; Cela dépend entièrement de la façon dont il est utilisé.


Vous ne voulez pas avoir à savoir qu'une méthode de swap n'est pas une sécurité. Cela va au cœur de la bonne ingénierie.



5
votes

J'ai toujours aimé ceci:

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;
}


0 commentaires

7
votes

Dans ma tentative de demander le prix Uberhacker, je soumets ce qui suit. Pour mes tests, j'ai utilisé un tableau source de 8.192 octets et appelé swapx2 100 000 fois: xxx

My Benchmarking indique que cette version est supérieure à 1,8 fois plus rapide que le code soumis dans la question initiale.


1 commentaires

Ce code a un bug qu'il lit et écrit en dehors du tampon lorsque la longueur est impair. Cela pourrait entraîner une corruption de la mémoire ou une faute de page. Changer "BP + Source.Length" dans "BP + (source.length & 0xFFFFFFE)". Ou jeter une exception lorsque la taille du tampon est impair.



2
votes

méthode suivante, dans mon test, presque 3 fois plus rapide comme réponse acceptée. (Toujours plus rapide sur plus de 3 caractères ou six octets, un peu plus lentement sur moins ou égal à trois caractères ou six octets.) ( Notez que la réponse acceptée peut lire / écrire en dehors des limites de la matrice. )

(mise à jour lorsque vous avez un pointeur, il n'est pas nécessaire d'appeler la propriété pour obtenir la longueur. L'utilisation de ce pointeur est un peu plus rapide, mais nécessite une vérification d'exécution ou, comme dans l'exemple suivant, une configuration de projet à construire pour Chaque plate-forme. Définissez X86 et X64 sous chaque configuration.) xxx

cinq tests avec 300.000 fois 8192 octets

  • SWAPV2: 1055, 1051, 1043, 1041, 1044
  • Swapx2: 2802, 2803, 2803, 2805, 2805

    cinq tests avec 50.000.000 fois 6 octets

    • SWAPV2: 1092, 1085, 1086, 1087, 1086
    • Swapx2: 1018, 1019, 1015, 1017, 1018

      Mais si les données sont grandes et que la performance importe vraiment, vous pouvez utiliser SSE ou AVX. (13 fois plus rapide.) https://pastebin.com/wafk275u

      Test 5 Times, 100 000 boucles avec 8192 octets ou 4096 caractères

      • Swapx2: 226, 223, 225, 226, 227 min: 223
      • SWAPV2: 113, 111, 112, 114, 112 min: 111
      • Swapa2: 17, 17, 17, 17, 17, 16 min: 16

0 commentaires