Schnellste de-interleave-operation in C?

Habe ich einen Zeiger auf ein array von bytes mixed enthält die verschachtelte bytes der zwei unterschiedliche arrays array1 und array2. Sagen mixed sieht ungefähr so aus:

a1b2c3d4...

Was ich tun müssen, ist de-verschachteln der bytes, so bekomme ich array1 = abcd... und array2 = 1234.... Ich weiß, die Länge von mixed vor der Zeit, und die Längen der array1 und array2 sind gleichwertig, beide gleich mixed /2.

Hier ist meine aktuelle Umsetzung (array1 und array2 sind bereits zugeordnet):

int i, j;
int mixedLength_2 = mixedLength / 2;
for (i = 0, j = 0; i < mixedLength_2; i++, j += 2)
{
    array1[i] = mixed[j];
    array2[i] = mixed[j+1];
}

Dies vermeidet teure Multiplikation oder division Operationen, aber immer noch nicht schnell genug laufen. Ich hoffe, dass es so etwas wie memcpy braucht man ein indexer, der die low-level-block-copy-Vorgänge, um den Prozess zu beschleunigen. Gibt es eine schnellere Umsetzung, als das, was ich derzeit habe?

Bearbeiten

Die Zielplattform ist Objective-C für iOS und Mac. Eine schnelle operation ist wichtiger für iOS-Geräte, also eine Lösung, die sich an iOS-spezifisch wäre besser als nichts.

Update

Danke an alle für die Antworten, vor allem Stephen Canon, Graham Lee, und Mecki. Hier ist mein "master" - Funktion, die verwendet Stephen ' s NEON Interna, wenn verfügbar und sonst Graham union Cursor mit einer reduzierten Anzahl von Iterationen wie vorgeschlagen von Mecki.

void interleave(const uint8_t *srcA, const uint8_t *srcB, uint8_t *dstAB, size_t dstABLength)
{
#if defined __ARM_NEON__
    //attempt to use NEON intrinsics

    //iterate 32-bytes at a time
    div_t dstABLength_32 = div(dstABLength, 32);
    if (dstABLength_32.rem == 0)
    {
        while (dstABLength_32.quot --> 0)
        {
            const uint8x16_t a = vld1q_u8(srcA);
            const uint8x16_t b = vld1q_u8(srcB);
            const uint8x16x2_t ab = { a, b };
            vst2q_u8(dstAB, ab);
            srcA += 16;
            srcB += 16;
            dstAB += 32;
        }
        return;
    }

    //iterate 16-bytes at a time
    div_t dstABLength_16 = div(dstABLength, 16);
    if (dstABLength_16.rem == 0)
    {
        while (dstABLength_16.quot --> 0)
        {
            const uint8x8_t a = vld1_u8(srcA);
            const uint8x8_t b = vld1_u8(srcB);
            const uint8x8x2_t ab = { a, b };
            vst2_u8(dstAB, ab);
            srcA += 8;
            srcB += 8;
            dstAB += 16;
        }
        return;
    }
#endif

    //if the bytes were not aligned properly
    //or NEON is unavailable, fall back to
    //an optimized iteration

    //iterate 8-bytes at a time
    div_t dstABLength_8 = div(dstABLength, 8);
    if (dstABLength_8.rem == 0)
    {
        typedef union
        {
            uint64_t wide;
            struct { uint8_t a1; uint8_t b1; uint8_t a2; uint8_t b2; uint8_t a3; uint8_t b3; uint8_t a4; uint8_t b4; } narrow;
        } ab8x8_t;

        uint64_t *dstAB64 = (uint64_t *)dstAB;
        int j = 0;
        for (int i = 0; i < dstABLength_8.quot; i++)
        {
            ab8x8_t cursor;
            cursor.narrow.a1 = srcA[j  ];
            cursor.narrow.b1 = srcB[j++];
            cursor.narrow.a2 = srcA[j  ];
            cursor.narrow.b2 = srcB[j++];
            cursor.narrow.a3 = srcA[j  ];
            cursor.narrow.b3 = srcB[j++];
            cursor.narrow.a4 = srcA[j  ];
            cursor.narrow.b4 = srcB[j++];
            dstAB64[i] = cursor.wide;
        }
        return;
    }

    //iterate 4-bytes at a time
    div_t dstABLength_4 = div(dstABLength, 4);
    if (dstABLength_4.rem == 0)
    {
        typedef union
        {
            uint32_t wide;
            struct { uint8_t a1; uint8_t b1; uint8_t a2; uint8_t b2; } narrow;
        } ab8x4_t;

        uint32_t *dstAB32 = (uint32_t *)dstAB;
        int j = 0;
        for (int i = 0; i < dstABLength_4.quot; i++)
        {
            ab8x4_t cursor;
            cursor.narrow.a1 = srcA[j  ];
            cursor.narrow.b1 = srcB[j++];
            cursor.narrow.a2 = srcA[j  ];
            cursor.narrow.b2 = srcB[j++];
            dstAB32[i] = cursor.wide;
        }
        return;
    }

    //iterate 2-bytes at a time
    div_t dstABLength_2 = div(dstABLength, 2);
    typedef union
    {
        uint16_t wide;
        struct { uint8_t a; uint8_t b; } narrow;
    } ab8x2_t;

    uint16_t *dstAB16 = (uint16_t *)dstAB;
    for (int i = 0; i < dstABLength_2.quot; i++)
    {
        ab8x2_t cursor;
        cursor.narrow.a = srcA[i];
        cursor.narrow.b = srcB[i];
        dstAB16[i] = cursor.wide;
    }
}

void deinterleave(const uint8_t *srcAB, uint8_t *dstA, uint8_t *dstB, size_t srcABLength)
{
#if defined __ARM_NEON__
    //attempt to use NEON intrinsics

    //iterate 32-bytes at a time
    div_t srcABLength_32 = div(srcABLength, 32);
    if (srcABLength_32.rem == 0)
    {
        while (srcABLength_32.quot --> 0)
        {
            const uint8x16x2_t ab = vld2q_u8(srcAB);
            vst1q_u8(dstA, ab.val[0]);
            vst1q_u8(dstB, ab.val[1]);
            srcAB += 32;
            dstA += 16;
            dstB += 16;
        }
        return;
    }

    //iterate 16-bytes at a time
    div_t srcABLength_16 = div(srcABLength, 16);
    if (srcABLength_16.rem == 0)
    {
        while (srcABLength_16.quot --> 0)
        {
            const uint8x8x2_t ab = vld2_u8(srcAB);
            vst1_u8(dstA, ab.val[0]);
            vst1_u8(dstB, ab.val[1]);
            srcAB += 16;
            dstA += 8;
            dstB += 8;
        }
        return;
    }
#endif

    //if the bytes were not aligned properly
    //or NEON is unavailable, fall back to
    //an optimized iteration

    //iterate 8-bytes at a time
    div_t srcABLength_8 = div(srcABLength, 8);
    if (srcABLength_8.rem == 0)
    {
        typedef union
        {
            uint64_t wide;
            struct { uint8_t a1; uint8_t b1; uint8_t a2; uint8_t b2; uint8_t a3; uint8_t b3; uint8_t a4; uint8_t b4; } narrow;
        } ab8x8_t;

        uint64_t *srcAB64 = (uint64_t *)srcAB;
        int j = 0;
        for (int i = 0; i < srcABLength_8.quot; i++)
        {
            ab8x8_t cursor;
            cursor.wide = srcAB64[i];
            dstA[j  ] = cursor.narrow.a1;
            dstB[j++] = cursor.narrow.b1;
            dstA[j  ] = cursor.narrow.a2;
            dstB[j++] = cursor.narrow.b2;
            dstA[j  ] = cursor.narrow.a3;
            dstB[j++] = cursor.narrow.b3;
            dstA[j  ] = cursor.narrow.a4;
            dstB[j++] = cursor.narrow.b4;
        }
        return;
    }

    //iterate 4-bytes at a time
    div_t srcABLength_4 = div(srcABLength, 4);
    if (srcABLength_4.rem == 0)
    {
        typedef union
        {
            uint32_t wide;
            struct { uint8_t a1; uint8_t b1; uint8_t a2; uint8_t b2; } narrow;
        } ab8x4_t;

        uint32_t *srcAB32 = (uint32_t *)srcAB;
        int j = 0;
        for (int i = 0; i < srcABLength_4.quot; i++)
        {
            ab8x4_t cursor;
            cursor.wide = srcAB32[i];
            dstA[j  ] = cursor.narrow.a1;
            dstB[j++] = cursor.narrow.b1;
            dstA[j  ] = cursor.narrow.a2;
            dstB[j++] = cursor.narrow.b2;
        }
        return;
    }

    //iterate 2-bytes at a time
    div_t srcABLength_2 = div(srcABLength, 2);
    typedef union
    {
        uint16_t wide;
        struct { uint8_t a; uint8_t b; } narrow;
    } ab8x2_t;

    uint16_t *srcAB16 = (uint16_t *)srcAB;
    for (int i = 0; i < srcABLength_2.quot; i++)
    {
        ab8x2_t cursor;
        cursor.wide = srcAB16[i];
        dstA[i] = cursor.narrow.a;
        dstB[i] = cursor.narrow.b;
    }
}
  • Nun, wenn die Eingabe tatsächlich interleaved, dann kann man nicht wirklich block-Kopie...
  • Welche Plattform[s] Sie sind targeting? Viele gut optimierte Bibliothek-Funktionen für die Ausführung dieser Operationen. Es gibt nichts in der C-standard-Bibliothek, jedoch.
  • Objective-C für iOS/Mac. Diese Optimierung ist besonders wichtig für iOS.
  • was bedeutet iOS und OS X, oder haben Sie Sorge, über den anderen Plattformen auch?
  • Bearbeitet mein Kommentar zu klären - iOS und OS X.
  • memcpy nicht funktioniert, aber ich bin der Hoffnung, für etwas gleich schnell sind.
  • Sollte nicht sein, dass viel Verbesserung, aber statt i < mixedLength / 2 schreiben Sie j < mixedLength und speichern Sie eine division pro iteration ohne eine temporäre variable.
  • Danke, hab ich aktualisiert den code entsprechend. Du hast Recht - es ist nicht genug von einer Verbesserung.
  • Sie können versuchen, das Lesen der Quell-array als ein array von kurzen (2-byte-Mengen) oder vielleicht sogar 4-oder 8-byte-Ganzzahlen. Store durch Extraktion von geraden und ungeraden Hälften mit Schichten und Masken. Nicht wirklich portabel, sondern Sie sollten etwas beschleunigen.
  • Die de-interleaved bytes übergeben werden, in eine third-party Bibliothek. Könnte ich das evtl ändern Sie das third-party-Bibliothek, sodass es Indizes anders, aber das wäre eine "alles-andere-ist-gescheitert" last resort".
  • Sie brauchen nicht zu ändern Ihre Schnittstelle. So etwas wie short a=((short*)mixed)[i]; array1[i] = a&0xFF; array2[i] = a>>8;.
  • Haben Sie schaute auf die Accelerate-framework-API? Sie werden zweifellos finden, was Sie nach gibt.
  • Ich denke, dass Sie verwenden können vunzp.8 für ein NEON-Teil des Programms. Es sieht aus wie Stephen gab es Sie unten. Siehe auch Codierung für NEON - Teil 5: Neuanordnung von Vektoren.

InformationsquelleAutor Anton | 2013-01-28
Schreibe einen Kommentar