カメヲラボ

主にプログラミングとお勉強全般について書いてます

どんな最適化やねん(4)

VC++の最適化ってどないやねん(2)

再びabs関数の速度比較に戻ります.前回(http://d.hatena.ne.jp/Ozy/20071111#p1)のコメント欄に書いたことのまとめですが,if文で分岐させたabs1とビット演算を使ったabs2は最適化によってかなりの速度差になることがわかりました.で,その時ふと思ったわけです.「標準関数のabsってどんなもんかな」と.

そんなわけで、abs1とabs2の速い方、つまりabs2と標準関数のabsを比較してみました.

テストコードは以下のような感じです.

  • test.c(の一部)
    for(i=0;i<ARRAY_SIZE;++i)
    {
      K=abs(a[i]); // or K=abs2(a[i]);
    }

Kはグローバル変数で,このように書いておいて最適化時にコードが丸々消されるのを防ぎます.コイツを/O2オプションでコンパイルしてやります.

  • abs2
; 52   :       K=abs2(a[i]);

  00070	8b 0a		 mov	 ecx, DWORD PTR [edx]
  00072	8b c1		 mov	 eax, ecx
  00074	c1 f8 1f	 sar	 eax, 31			; 0000001fH
  00077	8b e8		 mov	 ebp, eax
  00079	33 e9		 xor	 ebp, ecx
  0007b	2b e8		 sub	 ebp, eax
  0007d	83 c2 14	 add	 edx, 20			; 00000014H
  00080	83 ee 01	 sub	 esi, 1
  00083	89 2d 00 00 00
	00		 mov	 DWORD PTR _K, ebp
  00089	75 e5		 jne	 SHORT $LL3@main
  • abs
; 52   :       K=abs(a[i]);

  00077	8b 01		 mov	 eax, DWORD PTR [ecx]
  00079	99		 cdq
  0007a	33 c2		 xor	 eax, edx
  0007c	2b c2		 sub	 eax, edx
  0007e	83 c1 14	 add	 ecx, 20			; 00000014H
  00081	83 ee 01	 sub	 esi, 1
  00084	a3 00 00 00 00	 mov	 DWORD PTR _K, eax
  00089	75 ec		 jne	 SHORT $LL3@main

標準関数を使った場合,abs2を最適化したものに良く似ていますが,シフトは行わずにcdq命令を使ってedxレジスタを正→0,負→-1みたいなかんじでセットするわけですね.

へるみさんによれば,

μOPレベルでは同じものになりますが,cdqの方がコード長が短いので速くなることがあります(CPU依存).

私の手元にあるCPUだとすべて速くなったので,CPU依存とはいえ結構いろんなCPUで速くなりそうですね.それから,GCC 4.xではどないかなーと思ってたところ,(これもへるみさんのコメント)

ちなみにgcc4.2.2ではabs(), abs1(), abs2()どれを使っても同一のコードが生成されました.
addl $1, %ecx
sarl $31, %edx
xorl %edx, %eax
subl %edx, %eax

だそうです(゜□゜)
私もMinGWに付いてたgcc4.1.2で試したのですが,ちょっと違うコードでした.早いトコcygwinの標準gccが4.xになってくれれば楽に試せるのですが…(自前ビルドは相変わらず失敗しています^^;)


何気なく調べたら,結構奥深いですね.こういう話題はちょっと避け気味でやったきたのですが,ショートコーディングでもバイナリ埋め込みとか出てきましたし,ちゃんと追求しないといけませんね….