カメヲラボ

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

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

  • VC++の最適化ってどないやねん

単純なコード

main()
{
  int i,j;

  for(i=0;i<10000;++i)
  {
    for(j=0;j<100000;++j)
    {
      // 何もしない
    }
  }
}

コイツをGCC(3.4.4)で-O2コンパイルしてみると,

.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	$16, %eax
	movl	%esp, %ebp
	subl	$8, %esp
	andl	$-16, %esp
	call	__alloca
	call	___main
	xorl	%edx, %edx
L9:
	movl	$99999, %eax
	.p2align 4,,15
L8:
	decl	%eax
	jns	L8
	incl	%edx
	cmpl	$9999, %edx
	jle	L9
	leave
	ret

てな感じのコードになります.同じコードをVC++2005 Expressで/O2オプションをつけてコンパイルすると.

INCLUDELIB OLDNAMES

EXTRN	@__security_check_cookie@4:PROC
PUBLIC	_main
_TEXT	SEGMENT
_main	PROC						; COMDAT
  00000	33 c0		 xor	 eax, eax
  00002	c3		 ret	 0
_main	ENDP
_TEXT	ENDS
END

なんという最適化^^;

…かと思えば,次のようなコード

main()
{
  int i,j;

  for(i=0;i<10000;++i)
  {
    for(j=0;j<100000.0;++j)
    {
      // 何もしない
    }
  }
}

先程と違うのは,内側のループを100000から100000.0に変えただけですが,この場合はGCC

LC0:
	.long	1203982336
	.text
	.p2align 4,,15
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	$16, %eax
	movl	%esp, %ebp
	subl	$8, %esp
	andl	$-16, %esp
	call	__alloca
	call	___main
	flds	LC0
	xorl	%edx, %edx
	.p2align 4,,15
L10:
	xorl	%ecx, %ecx
	.p2align 4,,15
L9:
	incl	%ecx
	pushl	%ecx
	fildl	(%esp)
	fxch	%st(1)
	addl	$4, %esp
	fucom	%st(1)
	fnstsw	%ax
	fstp	%st(1)
	sahf
	ja	L9
	incl	%edx
	cmpl	$9999, %edx
	jle	L10
	fstp	%st(0)
	leave
	ret

で,VC++だと

INCLUDELIB OLDNAMES

EXTRN	@__security_check_cookie@4:PROC
PUBLIC	__real@40f86a0000000000
PUBLIC	_main
EXTRN	__fltused:DWORD
CONST	SEGMENT
__real@40f86a0000000000 DQ 040f86a0000000000r	; 100000
CONST	ENDS
_TEXT	SEGMENT
_j$ = -4						; size = 4
_main	PROC						; COMDAT
  00000	51		 push	 ecx
  00001	dd 05 00 00 00
	00		 fld	 QWORD PTR __real@40f86a0000000000
  00007	ba 10 27 00 00	 mov	 edx, 10000		; 00002710H
$LN6@main:
  0000c	33 c9		 xor	 ecx, ecx
$LN3@main:
  0000e	83 c1 01	 add	 ecx, 1
  00011	89 0c 24	 mov	 DWORD PTR _j$[esp+4], ecx
  00014	db 04 24	 fild	 DWORD PTR _j$[esp+4]
  00017	d8 d9		 fcomp	 ST(1)
  00019	df e0		 fnstsw	 ax
  0001b	f6 c4 05	 test	 ah, 5
  0001e	7b ee		 jnp	 SHORT $LN3@main
  00020	83 ea 01	 sub	 edx, 1
  00023	75 e7		 jne	 SHORT $LN6@main
  00025	dd d8		 fstp	 ST(0)
  00027	33 c0		 xor	 eax, eax
  00029	59		 pop	 ecx
  0002a	c3		 ret	 0
_main	ENDP
_TEXT	ENDS
END

ちょっと変えただけなのに,VC++の最適化がエライ違います.これら4種類のコードを実行すると,後者のVC++バージョンだけやたらと遅いのです.私はGCCが吐いたアセンブリしか見てなかったので,こんなことになっているとは全く考えていませんでした.


どうしてこんなことに悩まされたかと申しますと,ゴルフ脳とも言うべきか10000とか100000という値を1e4とか1e5と,浮動小数で書いてしまう癖が付いてしまっているわけです.Cの場合はあまり型にうるさくありませんし,普段このように書いていてもほとんど問題になりません.しかしVC++だとものすごく差が出てしまうとなると,VC++はつくづくゴルフ向けではないなーと感じます.


…っちゅうか,これだけのことで実行速度が桁違いに変わってしまう最適化って,どないやねん(;´д`)