読者です 読者をやめる 読者になる 読者になる

徒然なる日々を送るソフトウェアデベロッパーの記録(2)

技術上思ったことや感じたことを気ままに記録していくブログです。さくらから移設しました。

neon をいじってみる

Raspberry Pi2 の CPU は SIMD 命令(neon)をサポート
しているので、例題を書いてみた。お題は
"Professional Embedded ARM development"(Wrox)
にあったもので、RGB空間のベタ詰めの画像ファイルを gray
画像に変換する、というものである。変換式はこちら。

  • Gray = 0.3*R + 0.59*G + 0.11*B

256倍する。

  • 256*Gray = 77*R + 151*G + 28*B

∴ Gray = (77*R + 151*G + 28*B) / 256

掛け算および足し算の時は q レジスタを使用し、画像の
ロード時は d レジスタを使用する。256 で割ることと
右に8ビットシフトすることは同等なので、シフト後に q
レジスタから d レジスタに縮小すれば8ドット分の gray
画像が算出できる。アセンブラで書いてみたのがこちら。

.arch armv7-a
.fpu neon-vfpv4
.text
.align 2
.global color2gray
.type color2gray, %function
color2gray:
        @ color space is R[0]G[0]B[0]R[1]G[1]B[1]...
        @ r0: base address
        @ r1: store address
        @ r2: length of file (not bytes)
        push {r4-r8}
        mov r4, #77
        mov r5, #151
        mov r6, #28
        mov r8, r2
        vdup.8 d4, r4
        vdup.8 d5, r5
        vdup.8 d6, r6
        lsr r8, r8, #3
        movs r8, r8
        beq color2gray_boundary

color2gray_loop:
        vld3.8 {d7, d8, d9}, [r0]!
        vmull.u8 q3, d7, d4     @ Red
        vmlal.u8 q3, d8, d5     @ Green
        vmlal.u8 q3, d9, d6     @ Blue
        vshrn.u16 d3, q3, #3
        vst1.8 {d3}, [r1]!
        subs r8, #1
        bne color2gray_loop

color2gray_boundary:
        mov r8, #7
        ands r2, r2, r8
        beq color2gray_end
color2gray_each:
        ldrb r7, [r0]   @ Red
        mul r8, r4, r7
        add r0, r0, #1
        ldrb r7, [r0]   @ Green
        mla r8, r5, r7, r8
        add r0, r0, #1
        ldrb r7, [r0]   @ Blue
        mla r8, r6, r7, r8
        lsr r8, r8, #8
        strb r8, [r1]
        subs r2, #1
        add r0, r0, #1
        add r1, r1, #1
        bne color2gray_each

color2gray_end:
        pop {r4-r8}
        bx lr

C プログラムからは

void color2gray(const unsigned char *rgb, unsigned char *gray, int len);

で呼び出せる。ここで len はバイト数ではなく、
ピクセル数を指定する。

8ピクセルに満たない領域ができた場合は CPU 側で
ループを回すようにした。(color2gray_each の部分)

3.8メガバイトのファイルを処理するのに 11 ミリ秒
くらい要した。
C を使って CPU でループを回すよりだいぶ速い感じ。