いやぁ……。今日初めて驚愕の事実を知ったわ。
昨日の検証で分散処理できそうな所はコア数分に分散した方が早い、という事が分かったから画像拡大回転処理の部分もコア数分に分散処理する様にしたんですよ。(scalaだったら簡単にできるのになぁ……。結構面倒でした。)
私のCPUはデュアルコアだからそれで実行速度も大体二倍になったのは良いんです。
問題はその次でした。
アルゴリズム的な事以外でもうちょっと高速化できないかなぁ〜と弄っていたんです。(ちなみに、現在のアルゴリズムの前は、全てを足し算だけで計算するもうちょっとだけ早いアルゴリズムを採用していたんですが、(それが原因かは分かっていないのですが)描画に誤差が出る、というバグが発生していたので止めました。)
if内の評価回数を減らしたりちょこっと弄った程度じゃ、全画面表示時に一回全体を更新するのに62milisecかかるのが60milisecに減った程度で全然変わらない。
どん詰まりだったので、何となく
*512と/2を<<9と>>1のビット演算に書き換えてみた。
絶対変わるわけない、私はそう確信していました。
だって、今時普通に考えたらこんなことコンパイラ側で最適化してくれているだろ?
60milisec→35milisec
……えええええええぇぇぇぇええええええ!!???????????
シングルスレッドから2スレッドにしたのと同じぐらい効果が出た。
ここから言えることはただ一つ。
JAVAコンパイラは計算の最適化を一切してくれていない!
今までコンパイラを信用して(面倒くさかったので)シフト演算に書き換えていないところがいっぱいあるが、まさか全く最適化されていなかったとは……
いや、勝手に信用していただけだけどさ、Cとかはちゃんと最適化しているらしいべ!?
1024で割ったあまりとかも、「a%1024」を「a&1023」に書き直してくれている物だと勝手に思い込んでいた。(2^nで割ったあまりは2^n -1 とビット積取ればあまりになります。負数でもちゃんと正数のあまりになるのでJAVAの予算より便利。)
JAVAでシフト演算、ビット演算で計算できるところは手動で書きましょう。
もしかしたら計算順序の前後を入れ替えるだけでも高速化する場所とかあるかもねぇ……。キャストが絡む辺りは絶対あると思う。
追記は実際に逆コンパイルして確かめてみた話と計算速度をちょっと調べてみた話だよ。
んじゃ、以下のソースを実際にコンパイル
public static void main(String arg){
int a = 2000,b;
b = a/2;
b = a>>1;
b = a*1024;
b = a<<10;
b = a%1024;
b = a&1023;
a=a;//ちょっとコンパイラを虐めてみただけ。
}
で、逆コンパイル。
public static void main(String args)
{
char c = '\u07D0';//←何故かchar型に最適化
int i = c / 2;
i = c >> 1;
i = c * 1024;
i = c << 10;
i = c % 1024;
i = c & 0x3ff;
c = c;//←これ消えてねぇえええwww
}
char型に勝手に最適化するくせに途中の式とか、最後の自己代入とか最適化してくれてないことが分かりました。
最後の自己代入は警告出すくせにねぇ……。
さて、これらの計算の違いが実際にどれぐらい実行速度に影響を与えるのか私の環境でだが、調べてみた。
かけ算
どうやらcpu側で最適化される様で速度差測定不能。
かける値を1023とすると1.3倍差が出るが、かけ算ではシフト演算を気にすることはなさそうだ。
割り算
10倍と顕著な差が出た
なるべくシフト演算に書き直そう。
予算
大体6倍近い差が出る。
こちらも出来るならビット演算に書き換えることをお奨めする。
シフト演算とシフト演算、およびシフト演算とビット演算
シフト演算でもシフト数が違うと差が出るのか調べたが、測定不能。
同様にシフト演算とビット演算を比較したが、こちらも測定不能だ。