8

NEON ベクトル命令セットを使用して、iOS で画像フォーマット変換を最適化しようとしています。これは似たようなデータの束を処理するため、これによく対応すると思いました。

ただし、私の試みはそれほどうまくいきませんでした.単純なc実装に対してわずかなスピードアップしか達成できませんでした:

for(int i = 0; i < pixelCount; ++i, ++inPixel32) {
    const unsigned int r = ((*inPixel32 >> 0 ) & 0xFF);
    const unsigned int g = ((*inPixel32 >> 8 ) & 0xFF);
    const unsigned int b = ((*inPixel32 >> 16) & 0xFF);
    *outPixel16++ = ((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0);
}

iPad 2 での 1 メガピクセルの画像配列:

形式は [min avg max n=タイマー サンプル数] ミリ秒単位です。

C: [14.446 14.632 18.405 n=1000]ミリ秒

NEON: [11.920 12.032 15.336 n=1000]ミリ秒

NEON 実装での私の試みは以下のとおりです。

    int i;
const int pixelsPerLoop = 8;
for(i = 0; i < pixelCount; i += pixelsPerLoop, inPixel32 += pixelsPerLoop, outPixel16 += pixelsPerLoop) {
    //Read all r,g,b pixels into 3 registers
    uint8x8x4_t rgba  = vld4_u8(inPixel32);
    //Right-shift r,g,b as appropriate
    uint8x8_t r = vshr_n_u8(rgba.val[0], 3);
    uint8x8_t g = vshr_n_u8(rgba.val[1], 2);
    uint8x8_t b = vshr_n_u8(rgba.val[2], 3);

    //Widen b
    uint16x8_t r5_g6_b5 = vmovl_u8(b);
    //Widen r
    uint16x8_t r16 = vmovl_u8(r);
    //Left shift into position within 16-bit int
    r16 = vshlq_n_u16(r16, 11);
    r5_g6_b5 |= r16;

    //Widen g
    uint16x8_t g16 = vmovl_u8(g);
    //Left shift into position within 16-bit int
    g16 = vshlq_n_u16(g16, 5);

    r5_g6_b5 |= g16;

    //Now write back to memory
    vst1q_u16(outPixel16, r5_g6_b5);        
}
//Do the remainder on normal flt hardware

コードは LLVM 3.0 を介して次のようにコンパイルされました (.loc と余分なラベルは削除されています)。

_DNConvert_ARGB8888toRGB565:
    push    {r4, r5, r7, lr}
    mov r9, r1
    mov.w   r12, #0
    add r7, sp, #8
    cmp r2, #0
    mov.w   r1, #0
    it  ne
    movne   r1, #1
    cmp r0, #0
    mov.w   r3, #0
    it  ne
    movne   r3, #1
    cmp.w   r9, #0
    mov.w   r4, #0
    it  ne
    movne   r4, #1
    tst.w   r9, #3
    bne LBB0_8
    ands    r1, r3
    ands    r1, r4
    cmp r1, #1
    bne LBB0_8
    movs    r1, #0
    lsr.w   lr, r9, #2
    cmp.w   r1, r9, lsr #2
    bne LBB0_9
    mov r3, r2
    mov r5, r0
    b   LBB0_5
LBB0_4:
    movw    r1, #65528
    add.w   r0, lr, #7
    movt    r1, #32767
    ands    r1, r0
LBB0_5:
    mov.w   r12, #1
    cmp r1, lr
    bhs LBB0_8
    rsb r0, r1, r9, lsr #2
    mov.w   r9, #63488
    mov.w   lr, #2016
    mov.w   r12, #1
LBB0_7:
    ldr r2, [r5], #4
    subs    r0, #1
    and.w   r1, r9, r2, lsl #8
    and.w   r4, lr, r2, lsr #5
    ubfx    r2, r2, #19, #5
    orr.w   r2, r2, r4
    orr.w   r1, r1, r2
    strh    r1, [r3], #2
    bne LBB0_7
LBB0_8:
    mov r0, r12
    pop {r4, r5, r7, pc}
LBB0_9:
    sub.w   r1, lr, #1
    movs    r3, #32
    add.w   r3, r3, r1, lsl #2
    bic r3, r3, #31
    adds    r5, r0, r3
    movs    r3, #16
    add.w   r1, r3, r1, lsl #1
    bic r1, r1, #15
    adds    r3, r2, r1
    movs    r1, #0
LBB0_10:
    vld4.8  {d16, d17, d18, d19}, [r0]!
    adds    r1, #8
    cmp r1, lr
    vshr.u8 d20, d16, #3
    vshr.u8 d21, d17, #2
    vshr.u8 d16, d18, #3
    vmovl.u8    q11, d20
    vmovl.u8    q9, d21
    vmovl.u8    q8, d16
    vshl.i16    q10, q11, #11
    vshl.i16    q9, q9, #5
    vorr    q8, q8, q10
    vorr    q8, q8, q9
    vst1.16 {d16, d17}, [r2]!
Ltmp28:
    blo LBB0_10
    b   LBB0_4

完全なコードはhttps://github.com/darknoon/DNImageConvertで入手できます。

4

5 に答える 5

10

これが、XCode用に手動で最適化されたNEON実装の準備ができていることです。

/* IT DOESN'T WORK!!! USE THE NEXT VERSION BELOW.
 * BGRA2RGB565.s
 *
 * Created by Jake "Alquimista" Lee on 11. 11. 1..
 * Copyright 2011 Jake Lee. All rights reserved.
 */


    .align 2
    .globl _bgra2rgb565_neon
    .private_extern _bgra2rgb565_neon

// unsigned int * bgra2rgb565_neon(unsigned int * pDst, unsigned int * pSrc, unsigned int count);


//ARM
pDst        .req    r0
pSrc        .req    r1
count       .req    r2

//NEON
blu         .req    d16
grn         .req    d17
red         .req    d18
alp         .req    d19
rg          .req    red
gb          .req    blu

_bgra2rgb565_neon:
    pld     [pSrc]
    tst     count, #0x7
    movne   r0, #0
    bxne    lr

loop:
    pld     [pSrc, #32]
    vld4.8  {blu, grn, red, alp}, [pSrc]!
    subs    count, count, #8
    vshr.u8 red, red, #3
    vext.8  rg, grn, red, #5
    vshr.u8 grn, grn, #2
    vext.8  gb, blu, grn, #3
    vst2.8  {gb, rg}, [pDst]!
    bgt     loop

    bx      lr

このバージョンは、あなたが提案したものより何倍も速くなります:

  • PLDによるキャッシュヒット率の増加

  • 「長い」への変換は必要ありません

  • ループ内の命令が少ない

ただし、まだ最適化の余地はありますが、ループを変更して、8ではなく反復ごとに16ピクセルを変換することができます。次に、2つのストールを完全に回避するように命令をスケジュールできます(これは、この8/反復バージョンでは単純に不可能です)。上記)、さらにNEONの二重発行機能の恩恵を受けます。

コードが理解しにくくなるため、これは行いませんでした。

VEXTが何をすることになっているのかを知ることは重要です。

今それはあなた次第です。:)

このコードがXcodeで正しくコンパイルされていることを確認しました。正しく動作することは確かですが、テスト環境がないため、これを保証することはできません。故障の場合はお知らせください。それに応じて修正します。

cya

================================================== ============================

さて、これが改良版です。

VSRI命令の性質上、ターゲット以外の2つのオペランドを許可しないため、レジスタの割り当てに関してより堅牢なオペランドを作成することはできませんでした。

ソース画像の画像形式を確認してください。(要素の正確なバイト順序)

iOSのデフォルトでネイティブなB、G、R、Aでない場合、アプリケーションはiOSによる内部変換の影響を大きく受けます。

何らかの理由でこれを変更することが絶対に不可能な場合は、私に知らせてください。それに合わせて新しいバージョンを書きます。

PS:関数プロトタイプの開始時にアンダースコアを削除するのを忘れました。今ではなくなっています。

/*
 * BGRA2RGB565.s
 *
 * Created by Jake "Alquimista" Lee on 11. 11. 1..
 * Copyright 2011 Jake Lee. All rights reserved.
 *
 * Version 1.1
 * - bug fix
 *
 * Version 1.0
 * - initial release
 */


    .align 2
    .globl _bgra2rgb565_neon
    .private_extern _bgra2rgb565_neon

// unsigned int * bgra2rgb565_neon(unsigned int * pDst, unsigned int * pSrc, unsigned int count);


//ARM
pDst        .req    r0
pSrc        .req    r1
count       .req    r2

//NEON
blu         .req    d16
grn         .req    d17
red         .req    d18
alp         .req    d19

gb          .req    grn
rg          .req    red

_bgra2rgb565_neon:
    pld     [pSrc]
    tst     count, #0x7
    movne   r0, #0
    bxne    lr

.loop:
    pld     [pSrc, #32]
    vld4.8  {blu, grn, red, alp}, [pSrc]!
    subs    count, count, #8

    vsri.8  red, grn, #5
    vshl.u8 gb, grn, #3
    vsri.8  gb, blu, #3

    vst2.8  {gb, rg}, [pDst]!
    bgt     .loop

    bx      lr
于 2011-11-01T09:20:58.440 に答える
0

vld4_u8() の代わりに vld4q_u8() を使用し、それに応じて残りのコードを調整することもできます。問題がどこにあるのかを特定するのは困難ですが、アセンブラはそれほど悪くはありません。

于 2011-10-13T23:18:51.887 に答える
0

(私は NEON に精通しておらず、Ipad2 のメモリ システムにも詳しくありませんが、これは、今日の SIMD 拡張の初期の前身である 88110 ピクセル演算で使用していたものです)

メモリのレイテンシはどのくらいですか?

内部ループを展開し、ARM がメモリから「次の」値をプルしている間に「前の」値で NEON 命令を実行することで、それを隠すことができますか? NEON マニュアルをざっと読んでみると、ARM 命令と NEON 命令を並行して実行できることがわかります。

于 2011-10-14T11:41:27.040 に答える
0

vld4_u8 を vld4q_u8 に変換してもパフォーマンスが向上するとは思いません。

コードは十分に単純に見えます。私は ASM が苦手なので、深く調べるには時間がかかります。

ネオンは十分にシンプルに見えます。しかし、 r5_g6_b5 |= g16がvorrq_u16の代わりに使用されていることについて、私は確信が持てません。

最適化レベルも見てください。私が聞いた限りでは、ネオン コードの最適化レベルは最大 1 になります。したがって、DEFAULT による参照の最適化のレベルが異なる可能性があるため、参照コードとネオン コードの両方でデフォルトの最適化が考慮されている場合、パフォーマンスが異なる場合があります。違う。

現在のコードを改善できるネオンの領域は見つかりません。

于 2011-10-18T13:08:47.107 に答える