今日は、ARM CPUで出来るだけ計算を沢山行い、 ARM CPU自身の機能をしゃぶりつくすことについて書いてみる。
きっかけ
twitterのつぶやきに、YUV422からRGB565への変換が遅いというのが、この記事を書くきっかけになった。
元のコードは高級言語で書かれているようである。
チューニング案
- コンパイラの最適化を借りる
この方法が順当である。ポータビリティも上がり非常に良い。 - カリカリのアセンブラチューニング
ふつうはしない。
今回は後者を行ってみる
対象ターゲットはARM
その前に ARM CPU についての詳細は wikipedia 等を参照してもらうとして主な特徴は
- 32bit RISC CPU
- 16本の32bit汎用レジスタ
- 組込み向け/省電力向け用
となり、最近では携帯電話やポータブルゲーム機等にも採用されている。
ALU周り
今回、計算処理について語りたい。計算処理ということでALU周りについて書いておく。
ARMのALUは メインとなるALU 以外に、前段にパラレルシフタを配置している。
いきなりではあるが、以下のコードを書いたとする
ADD R0,R1,R2 ADD R3,R4,R5, LSL #2
- 最初の命令は R1 と R2 の値を足して R0 に代入
先ほどの図で示すと、Rn が R0 , Rm と R1 , R2 と Rp と対応する。
(この時のパラレルシフタは無視してください) - 次の命令は R5の値を左に#2ビットシフトして、R4 の値を足して R3 に代入
先ほどの図で示すと、R5 が Rp と対応しており、# が 2 となる。さらにR4がRmと対応して
RnがR3と対応する。
これを踏まえると、ある値がR1にありそれを5倍するとき以下の1命令で済む
ADD R1,R1,R1, LSL #2
R1を左に2ビットシフトして、それをR1と足し、さらにそれを R1 に入れる つまり R1 <- R1 + (R1 << 2) となる。
YUV422からRGBへの変換
変換式
R = 1.000Y + 1.402V G = 1.000Y - 0.344U - 0.714V B = 1.000Y + 1.772U
以上の変換式が一般的な変換式のようである。
ARMへの対応と最適化
本来だと浮動小数点を用い計算を行うが、最適化の為に以下のように考えてみる。
R = 1.000Y + 1.5V G = 1.000Y - 0.375U - 0.75V B = 1.000Y + 1.75U
変更点は
- 1.402V を 1.5V へ
- 0.344U を 0.375U へ
- 0.714V を 0.75V へ
- 1.772U を 1.75U へ
この変更を行い、最適化を行っている。
ちなみに
- 1.5V = (Vx2 + V) / 2 = 3V/2
- 0.375U = (Ux2+U)/8 = 3U/8
- 0.75V = (Vx4+Vx2)/8 = 6V/8
- 1.75U = (Ux4+Ux2)/8 +U = 6U/8 + U
となり、基本的に2のべき乗と足し算、2のべき乗の割り算で出来るようにした。 なお、1.772U を 1.75U は乱暴のような気がする。57/32 = 1.78 という案もあるが、 この場合、57を作るのに(32+16+8+1)のように多項式が増える。
実装
まずは 1.5V を作ってみる 例えば、R6にVがあり、R0に1.5V を代入するとすると
ADD R1,R6,R6,LSL #1 MOV R0,R1,LSR #1
という2命令になる。なおこの1命令は見かけ上1クロックで動くため、2クロックで変換できる。
もし R7 に Y が入っており、R8にRだとすると
R = 1.000Y + 1.5V
は
ADD R1,R6,R6,LSL #1 MOV R0,R1,LSR #1 ADD R8,R7,R0
となる。
ここで1つ。
ADD R1,R6,R6,LSL #1 ADD R8,R7,R1,LSR #1
とすると2命令で済む。 ちなみに、この命令削減は...640x400の画像だと256000命令削減できる。
続き
このような手法でやっていくと最適化されたものができる。 この続きは流石に実機がないとつらい。(SheevaPlug持ってるけど)
さいごに
アセンブラでARM CPU を触りこむ場合、上記の事を踏まえるとより 高速になるかもしれない。
ふつうは、コンパイラの最適化を期待してください。



コメントする