5

RGBから画像を変換する必要があるiOSアプリを開発しています-> BGRAはかなり迅速です。可能であれば、NEON 組み込み関数を使用したいと考えています。単にコンポーネントを割り当てるよりも速い方法はありますか?

void neonPermuteRGBtoBGRA(unsigned char* src, unsigned char* dst, int numPix)
{
    numPix /= 8; //process 8 pixels at a time

    uint8x8_t alpha = vdup_n_u8 (0xff);

    for (int i=0; i<numPix; i++)
    {
        uint8x8x3_t rgb  = vld3_u8 (src);
        uint8x8x4_t bgra;

        bgra.val[0] = rgb.val[2]; //these lines are slow
        bgra.val[1] = rgb.val[1]; //these lines are slow 
        bgra.val[2] = rgb.val[0]; //these lines are slow

        bgra.val[3] = alpha;

        vst4_u8(dst, bgra);

        src += 8*3;
        dst += 8*4;
    }


}
4

2 に答える 2

9

ARMCC の逆アセンブリもそれほど高速ではありません。

  • 最適な指示を使用していない

  • VFP 命令と NEON 命令が混在しているため、毎回大きな問題が発生します。

これを試して :

  mov r2, r2, lsr #3
  vmov.u8, d3, #0xff
loop:
  vld3.8 {d0-d2}, [r0]!
  subs r2, r2, #1
  vswp d0, d2
  vst4.8 {d0-d3}, [r1]!
  bgt loop

  bx lr

私が提案したコードも完全には最適化されていませんが、さらに「実際の」最適化を行うと、読みやすさが著しく損なわれます。だから私はここでやめます。

于 2012-08-14T06:04:17.847 に答える
7

これはコンパイラに依存します。たとえば、上記のコードを armcc (5.01) でコンパイルして逆アセンブルすると、次のようになります (ループを配置するだけで、アルファ割り当てをループの外に移動しました)。

  18:   f420440d    vld3.8  {d4-d6}, [r0]!
  1c:   e2822001    add r2, r2, #1  ; 0x1
  20:   eeb01b45    fcpyd   d1, d5
  24:   eeb00b46    fcpyd   d0, d6
  28:   eeb02b44    fcpyd   d2, d4
  2c:   f401000d    vst4.8  {d0-d3}, [r1]!
  30:   e1520003    cmp r2, r3
  34:   bafffff7    blt 18 <neonPermuteRGBtoBGRA_armcc+0x18>

コードを gcc (4.4.3) でコンパイルし、再度逆アセンブルすると、

  40:   f967 040f   vld3.8  {d16-d18}, [r7]
  44:   46d6        mov lr, sl
  46:   ecca 0b06   vstmia  sl, {d16-d18}
  4a:   9d02        ldr r5, [sp, #8]
  4c:   ed8d 8b1a   vstr    d8, [sp, #104]
  50:   3718        adds    r7, #24
  52:   e8be 000f   ldmia.w lr!, {r0, r1, r2, r3}
  56:   f108 0801   add.w   r8, r8, #1  ; 0x1
  5a:   c50f        stmia   r5!, {r0, r1, r2, r3}
  5c:   eddd 0b24   vldr    d16, [sp, #144]
  60:   e89e 0003   ldmia.w lr, {r0, r1}
  64:   edcd 0b16   vstr    d16, [sp, #88]
  68:   eddd 0b22   vldr    d16, [sp, #136]
  6c:   edcd 0b18   vstr    d16, [sp, #96]
  70:   e885 0003   stmia.w r5, {r0, r1}
  74:   ed9d 0b26   vldr    d0, [sp, #152]
  78:   9d03        ldr r5, [sp, #12]
  7a:   ed8d 0b14   vstr    d0, [sp, #80]
  7e:   cd0f        ldmia   r5!, {r0, r1, r2, r3}
  80:   46ae        mov lr, r5
  82:   465d        mov r5, fp
  84:   c50f        stmia   r5!, {r0, r1, r2, r3}
  86:   e89e 000f   ldmia.w lr, {r0, r1, r2, r3}
  8a:   e885 000f   stmia.w r5, {r0, r1, r2, r3}
  8e:   9501        str r5, [sp, #4]
  90:   465d        mov r5, fp
  92:   2100        movs    r1, #0
  94:   2220        movs    r2, #32
  96:   4620        mov r0, r4
  98:   f7ff fffe   bl  0 <memset>
  9c:   cd0f        ldmia   r5!, {r0, r1, r2, r3}
  9e:   4625        mov r5, r4
  a0:   c50f        stmia   r5!, {r0, r1, r2, r3}
  a2:   f8dd c004   ldr.w   ip, [sp, #4]
  a6:   e89c 000f   ldmia.w ip, {r0, r1, r2, r3}
  aa:   e885 000f   stmia.w r5, {r0, r1, r2, r3}
  ae:   ecd4 0b08   vldmia  r4, {d16-d19}
  b2:   f946 000f   vst4.8  {d16-d19}, [r6]
  b6:   3620        adds    r6, #32
  b8:   45c8        cmp r8, r9
  ba:   dbc1        blt.n   40 <memset+0x40>

また、armcc を使用した場合、実行時間は 10 倍速くなりました。

関数のarmccで生成されたアセンブリコードをコンパイルすると(アルファがループに戻ったように見えます:))gcc(インラインアセンブリ)を使用します

  void neonPermuteRGBtoBGRA_gas(unsigned char* src, unsigned char* dst,
    int numPix) {
    asm(
            "        ASR      r3,r2,#31\n"
            "        VMOV.I8  d1,#0xff\n"
            "        ADD      r2,r2,r3,LSR #29\n"
            "        ASR      r3,r2,#3\n"
            "        MOV      r2,#0\n"
            "        CMP      r3,#0\n"
            "        BLE      end\n"
            "loop:\n"
            "        VLD3.8   {d4,d5,d6},[r0]!\n"
            "        ADD      r2,r2,#1\n"
            "        CMP      r3,r2\n"
            "        VMOV.F64 d3,d5\n"
            "        VMOV.F64 d2,d6\n"
            "        VMOV.F64 d5,d1\n"
            "        VMOV.F64 d0,d4\n"
            "        VST4.8   {d2,d3,d4,d5},[r1]!\n"
            "        BGT      loop\n"
            "end:\n"
            );
  }

gccでも同じ実行時間になります。

最後に、バイナリを逆アセンブルして、コンパイラが必要なものを生成するか、アセンブリを使用するかを確認することをお勧めします。

ところで、この関数の実行時間をさらに改善したい場合は、調べることをお勧めします

  1. armのPLD(プリロードデータ)命令
  2. ループのアンローリングなど、ループ内で可能なすべてのネオン命令を利用します (実際の帯域幅は、メモリからのデータのロード時間になることに気付くでしょう)。
于 2012-08-10T14:31:23.233 に答える