私は画像処理屋なのですが、ハードウェアに頼らず、Java側で画像処理をするとき漠然と「8bitならint型に詰め込む、16bitならlong型に詰め込む」とやってきていました。
それってどれほど正しいのか?
というわけで、ふいに思い立ったので計ってみた。
後述するソースで計ってみたところ結果は
int配列 32ミリ秒(16bitカラーなら単に倍の64ミリ秒でしょうね)
byte配列43ミリ秒
long配列43ミリ秒(8bitカラー)
long配列42ミリ秒(16bitカラー)
というわけで、私の予想通りの結果となりました。
ある程度理由を述べるなら、Javaの配列アクセス速度の問題です。
JavaはCの様に単にポインタの指す位置を読み取っているのではなく、範囲チェックをしてから読み込みをしています。故にめっちゃおそい。
だから、なるべく配列の読み込み書き込みは減らした方が良いのです。
byte型の配列にして4回読み込むよりも、やや面倒でもint型の配列から値を1回だけ読み込みレジストリに書き込んで、後はビット演算で処理した方が速いんですね。
ちなみに、32bitCPUでlong型の演算は遅いのでいったんint型に格納していますが、64bitのMacで計測しても何故かint型にいったん格納した方が速かった。
StopWatchは自作の時間計測クラス
static int[] intArray = new int[10000]; static byte[] byteArray = new byte[40000]; static long[] longArray = new long[5000]; static long[] longArrayfor16bitCollor= new long[10000]; static StopWatch stopWatch = StopWatch.createUnsafeMiliStopWatch(); static int A,R,G,B; public static void intArray(){ int c; stopWatch.start(); for(int t=0;t<1000;t++) for(int k=0;k<10000;k++){ c=intArray[k]; A += c >>>24; R += c >>18 & 0xff; G += c >> 8 &0xff; B += c & 0xff; } stopWatch.lapAndStop(); } public static void byteArray(){ stopWatch.start(); for(int t=0;t<1000;t++) for(int k=0;k<10000;k++){ A += byteArray[k*4]&0xff; R += byteArray[k*4+1]&0xff; G += byteArray[k*4+2]&0xff; B += byteArray[k*4+3]&0xff; } stopWatch.lapAndStop(); } public static void longArrayfor8BitCollor(){ int c; stopWatch.start(); for(int t=0;t<1000;t++) for(int k=0;k<10000;k++){ c = (int)(longArray[k>>1]>>>36*(k&1)); A += c >>>24; R += c >>18 & 0xff; G += c >> 8 &0xff; B += c & 0xff; } stopWatch.lapAndStop(); } public static void longArrayfor16bitCollor(){ long c; int cu,cd; stopWatch.start(); for(int t=0;t<1000;t++) for(int k=0;k<10000;k++){ c = longArrayfor16bitCollor[k]; cu = (int) (c>>32); cd = (int)c; A += cu >>>16; R += cu & 0xffff; G += cd >>> 16; B += cd & 0xffff; } stopWatch.lapAndStop(); } public static void busyWait(int time){ long t = System.currentTimeMillis(); long l = time*1000L; while(System.currentTimeMillis()-t<l){} } public static void main(String[] args) { //ネイティブにいったんコンパイル。 intArray(); byteArray(); longArrayfor8BitCollor(); longArrayfor16bitCollor(); intArray(); byteArray(); longArrayfor8BitCollor(); longArrayfor16bitCollor(); stopWatch.reset(); busyWait(5);//コンパイルやJVMの安定を待つためビジー待機する。 //測定開始 intArray(); byteArray(); longArrayfor8BitCollor(); longArrayfor16bitCollor(); long[] ll=stopWatch.getLapTime(); for(long lo:ll)System.out.printf("%d milisec\n",lo); }