本項では、実際に生成されるコードを元に、性能検証を行います。
やはり一番気になるのは性能面です。
VC++6.0 にて、コンパイルオプション /FAcs を指定してコンパイルを行い、
いくつかのケースでどのようなアセンブリコードが生成されるかを追跡してみました。
検証 1) 加算
▼ 検証用コード
{
Vec4_t vecA(0,0,0,0);
Vec4_t vecB(1,2,3,4);
Vec4_t vecC(5,6,7,8);
int i ;
for( i=0 ; i<10000 ; i++ ){
vecA.xyzw() += vecB.xyzw() + vecC.xyzw() ;
}
test(
" %f %f %f %f \n"
, vecA.x()
, vecA.y()
, vecA.z()
, vecA.w()
);
}
▼ コンパイル結果
; 50 : {
; 51 : Vec4_t vecA(0,0,0,0);
00014 d9 05 00 00 00
00 fld DWORD PTR __real@4@00000000000000000000
0001a 83 c4 04 add esp, 4
0001d c7 44 24 14 00
00 00 00 mov DWORD PTR _vecA$55484[esp+36], 0
00025 c7 44 24 18 00
00 00 00 mov DWORD PTR _vecA$55484[esp+40], 0
0002d c7 44 24 1c 00
00 00 00 mov DWORD PTR _vecA$55484[esp+44], 0
00035 b8 10 27 00 00 mov eax, 10000 ; 00002710H
$L55488:
; 52 : Vec4_t vecB(1,2,3,4);
; 53 : Vec4_t vecC(5,6,7,8);
; 54 :
; 55 : int i ;
; 56 : for( i=0 ; i<10000 ; i++ ){
; 57 : vecA.xyzw() += vecB.xyzw() + vecC.xyzw() ;
0003a d8 05 00 00 00
00 fadd DWORD PTR __real@4@4001c000000000000000
00040 48 dec eax
00041 d9 54 24 00 fst DWORD PTR _Result$57109[esp+32]
00045 d9 44 24 14 fld DWORD PTR _vecA$55484[esp+36]
00049 d8 05 00 00 00
00 fadd DWORD PTR __real@4@40028000000000000000
0004f d9 5c 24 04 fstp DWORD PTR _Result$57109[esp+36]
00053 d9 44 24 18 fld DWORD PTR _vecA$55484[esp+40]
00057 d8 05 00 00 00
00 fadd DWORD PTR __real@4@4002a000000000000000
0005d 8b 4c 24 04 mov ecx, DWORD PTR _Result$57109[esp+36]
00061 89 4c 24 14 mov DWORD PTR _vecA$55484[esp+36], ecx
00065 d9 5c 24 08 fstp DWORD PTR _Result$57109[esp+40]
00069 d9 44 24 1c fld DWORD PTR _vecA$55484[esp+44]
0006d d8 05 00 00 00
00 fadd DWORD PTR __real@4@4002c000000000000000
00073 8b 54 24 08 mov edx, DWORD PTR _Result$57109[esp+40]
00077 89 54 24 18 mov DWORD PTR _vecA$55484[esp+40], edx
0007b d9 5c 24 0c fstp DWORD PTR _Result$57109[esp+44]
0007f 8b 4c 24 0c mov ecx, DWORD PTR _Result$57109[esp+44]
00083 89 4c 24 1c mov DWORD PTR _vecA$55484[esp+44], ecx
00087 75 b1 jne SHORT $L55488
; 58 : }
; 59 :
; 60 : test(
; 61 : " %f %f %f %f \n"
; 62 : , vecA.x()
; 63 : , vecA.y()
; 64 : , vecA.z()
; 65 : , vecA.w()
; 66 : );
00089 8b 44 24 08 mov eax, DWORD PTR _Result$57109[esp+40]
0008d 8b d1 mov edx, ecx
0008f 8b 4c 24 04 mov ecx, DWORD PTR _Result$57109[esp+36]
00093 52 push edx
00094 8b 54 24 04 mov edx, DWORD PTR _Result$57109[esp+36]
00098 50 push eax
00099 51 push ecx
0009a 52 push edx
0009b 68 00 00 00 00 push OFFSET FLAT:??_C@_0P@HMKC@?7?...
000a0 dd d8 fstp ST(0)
000a2 e8 00 00 00 00 call ?test@@YAXPBDMMMM@Z ; test
; 67 : }
vecB , vecC の存在自体が最適化により消滅し、
vecB + vecC の結果が vecA に足しこまれる様子が確認できます。
検証 2) スカラ三重積
▼ 検証用コード
{
Vec4_t vecA(1,0,0,0);
Vec4_t vecB(0,1,0,0);
Vec4_t vecC(0,0,1,0);
float Temp = 0 ;
int i ;
for( i=0 ; i<10000 ; i++ ){
Temp += Dot( Cross( vecA.xyz() , vecB.xyz() ) , vecC.xyz() );
}
test(
" %f \n"
, Temp
);
}
▼ コンパイル結果
; 69 : {
; 70 : Vec4_t vecA(1,0,0,0);
; 71 : Vec4_t vecB(0,1,0,0);
; 72 : Vec4_t vecC(0,0,1,0);
; 73 :
; 74 : float Temp = 0 ;
000a7 d9 05 00 00 00
00 fld DWORD PTR __real@4@00000000000000000000
000ad 83 c4 14 add esp, 20 ; 00000014H
000b0 b8 10 27 00 00 mov eax, 10000 ; 00002710H
$L55506:
; 75 :
; 76 : int i ;
; 77 : for( i=0 ; i<10000 ; i++ ){
; 78 : Temp += Dot( Cross( vecA.xyz() , vecB.xyz() ) , vecC.xyz() );
000b5 d8 05 00 00 00
00 fadd DWORD PTR __real@4@3fff8000000000000000
000bb 48 dec eax
000bc 75 f7 jne SHORT $L55506
000be d9 5c 24 24 fstp DWORD PTR _Temp$55504[esp+28]
; 79 : }
; 80 :
; 81 : test(
; 82 : " %f \n"
; 83 : , Temp
; 84 : );
000c2 8b 44 24 24 mov eax, DWORD PTR _Temp$55504[esp+28]
000c6 6a 00 push 0
000c8 6a 00 push 0
000ca 6a 00 push 0
000cc 50 push eax
000cd 68 00 00 00 00 push OFFSET FLAT:??_C@_05EEJL@?7?...
000d2 e8 00 00 00 00 call ?test@@YAXPBDMMMM@Z ; test
; 85 : }
vecA , vecB , vecC の存在自体が最適化により消滅し、
結果だけが Temp に足しこまれる様子が確認できます。
検証 3) ベクトルと行列の乗算
▼ 検証用コード
{
float ParamA[4][4] = {
{ 1, 2, 3, 4}
, { 5, 6, 7, 8}
, { 9,10,11,12}
, {13,14,15,16}
};
Mat4x4_t matA( ParamA );
float ParamB[4][4] = {
{17,18,19,20}
, {21,22,23,24}
, {25,26,27,28}
, {29,30,31,32}
};
Mat4x4_t matB( ParamB );
Vec4_t vecB(1,2,3,4);
Vec4_t vecA(0,0,0,0);
int i ;
for( i=0 ; i<10000 ; i++ ){
vecA += vecB * (matA * matB) ;
}
test(
" %f %f %f %f \n"
, vecA.x()
, vecA.y()
, vecA.z()
, vecA.w()
);
}
▼ コンパイル結果
; 89 : {
; 90 : float ParamA[4][4] = {
; 91 : { 1, 2, 3, 4}
; 92 : , { 5, 6, 7, 8}
; 93 : , { 9,10,11,12}
; 94 : , {13,14,15,16}
; 95 : };
; 96 : Mat4x4_t matA( ParamA );
; 97 :
; 98 : float ParamB[4][4] = {
; 99 : {17,18,19,20}
; 100 : , {21,22,23,24}
; 101 : , {25,26,27,28}
; 102 : , {29,30,31,32}
; 103 : };
; 104 : Mat4x4_t matB( ParamB );
; 105 :
; 106 : Vec4_t vecB(1,2,3,4);
; 107 : Vec4_t vecA(0,0,0,0);
000d7 d9 05 00 00 00
00 fld DWORD PTR __real@4@00000000000000000000
000dd 83 c4 14 add esp, 20 ; 00000014H
000e0 c7 44 24 14 00
00 00 00 mov DWORD PTR _vecA$55528[esp+36], 0
000e8 c7 44 24 18 00
00 00 00 mov DWORD PTR _vecA$55528[esp+40], 0
000f0 c7 44 24 1c 00
00 00 00 mov DWORD PTR _vecA$55528[esp+44], 0
000f8 b8 10 27 00 00 mov eax, 10000 ; 00002710H
$L55530:
; 108 :
; 109 : int i ;
; 110 : for( i=0 ; i<10000 ; i++ ){
; 111 : vecA += vecB * (matA * matB) ;
000fd d8 05 00 00 00
00 fadd DWORD PTR __real@4@400c9a10000000000000
00103 48 dec eax
00104 d9 54 24 00 fst DWORD PTR _Result$83206[esp+32]
00108 d9 44 24 14 fld DWORD PTR _vecA$55528[esp+36]
0010c d8 05 00 00 00
00 fadd DWORD PTR __real@4@400ca0a0000000000000
00112 d9 5c 24 04 fstp DWORD PTR _Result$83206[esp+36]
00116 d9 44 24 18 fld DWORD PTR _vecA$55528[esp+40]
0011a d8 05 00 00 00
00 fadd DWORD PTR __real@4@400ca730000000000000
00120 8b 4c 24 04 mov ecx, DWORD PTR _Result$83206[esp+36]
00124 89 4c 24 14 mov DWORD PTR _vecA$55528[esp+36], ecx
00128 d9 5c 24 08 fstp DWORD PTR _Result$83206[esp+40]
0012c d9 44 24 1c fld DWORD PTR _vecA$55528[esp+44]
00130 d8 05 00 00 00
00 fadd DWORD PTR __real@4@400cadc0000000000000
00136 8b 54 24 08 mov edx, DWORD PTR _Result$83206[esp+40]
0013a 89 54 24 18 mov DWORD PTR _vecA$55528[esp+40], edx
0013e d9 5c 24 0c fstp DWORD PTR _Result$83206[esp+44]
00142 8b 4c 24 0c mov ecx, DWORD PTR _Result$83206[esp+44]
00146 89 4c 24 1c mov DWORD PTR _vecA$55528[esp+44], ecx
0014a 75 b1 jne SHORT $L55530
; 112 : }
; 113 :
; 114 : test(
; 115 : " %f %f %f %f \n"
; 116 : , vecA.x()
; 117 : , vecA.y()
; 118 : , vecA.z()
; 119 : , vecA.w()
; 120 : );
0014c 8b 44 24 08 mov eax, DWORD PTR _Result$83206[esp+40]
00150 8b d1 mov edx, ecx
00152 8b 4c 24 04 mov ecx, DWORD PTR _Result$83206[esp+36]
00156 52 push edx
00157 8b 54 24 04 mov edx, DWORD PTR _Result$83206[esp+36]
0015b 50 push eax
0015c 51 push ecx
0015d 52 push edx
0015e 68 00 00 00 00 push OFFSET FLAT:??_C@_0P@HMKC@?7?...
00163 dd d8 fstp ST(0)
00165 e8 00 00 00 00 call ?test@@YAXPBDMMMM@Z ; test
; 121 : }
vecB matA matB の存在自体が最適化により消滅し、
結果だけが matA に足しこまれる様子が確認できます。
検証 4) ベクトルと行列の乗算(検証 3 よりも複雑)
▼ 検証用コード
{
float ParamA[4][4] = {
{ 1, 2, 3, 4}
, { 5, 6, 7, 8}
, { 9,10,11,12}
, {13,14,15,16}
};
Mat4x4_t matA( ParamA );
float ParamB[4][4] = {
{17,18,19,20}
, {21,22,23,24}
, {25,26,27,28}
, {29,30,31,32}
};
Mat4x4_t matB( ParamB );
Vec4_t vecB(1,2,3,4);
Vec4_t vecA(0,0,0,0);
int i ;
for( i=0 ; i<10000 ; i++ ){
vecA += vecB * (matA * matB * matA * matB * matA) ;
}
test(
" %f %f %f %f \n"
, vecA.x()
, vecA.y()
, vecA.z()
, vecA.w()
);
}
▼ コンパイル結果
; 124 : {
; 125 : float ParamA[4][4] = {
; 126 : { 1, 2, 3, 4}
; 127 : , { 5, 6, 7, 8}
; 128 : , { 9,10,11,12}
; 129 : , {13,14,15,16}
; 130 : };
; 131 : Mat4x4_t matA( ParamA );
; 132 :
; 133 : float ParamB[4][4] = {
; 134 : {17,18,19,20}
; 135 : , {21,22,23,24}
; 136 : , {25,26,27,28}
; 137 : , {29,30,31,32}
; 138 : };
; 139 : Mat4x4_t matB( ParamB );
; 140 :
; 141 : Vec4_t vecB(1,2,3,4);
; 142 : Vec4_t vecA(0,0,0,0);
0001a d9 05 00 00 00
00 fld DWORD PTR __real@4@00000000000000000000
00020 83 c4 04 add esp, 4
00023 c7 84 24 20 02
00 00 00 00 80
3f mov DWORD PTR _ParamA$55484[esp+800], 1065353216 ; 3f800000H
0002e c7 84 24 24 02
00 00 00 00 00
40 mov DWORD PTR _ParamA$55484[esp+804], 1073741824 ; 40000000H
00039 c7 84 24 28 02
00 00 00 00 40
(中略)
; 143 :
; 144 : int i ;
; 145 : for( i=0 ; i<10000 ; i++ ){
; 146 : vecA += vecB * (matA * matB * matA * matB * matA) ;
002fb c7 44 24 20 00
00 7a 43 mov DWORD PTR _Result$75224[esp+800], 1132068864 ; 437a0000H
00303 c7 44 24 24 00
00 82 43 mov DWORD PTR _Result$75224[esp+804], 1132593152 ; 43820000H
0030b c7 44 24 28 00
(中略)
00833 b8 10 27 00 00 mov eax, 10000 ; 00002710H
$L55491:
00838 d8 05 00 00 00
00 fadd DWORD PTR __real@4@401cfb651e8000000000
0083e 48 dec eax
0083f d9 54 24 00 fst DWORD PTR _Result$140642[esp+800]
00843 d9 44 24 14 fld DWORD PTR _vecA$55489[esp+804]
00847 d8 05 00 00 00
00 fadd DWORD PTR __real@4@401d8f282c8000000000
0084d d9 5c 24 04 fstp DWORD PTR _Result$140642[esp+804]
00851 d9 44 24 18 fld DWORD PTR _vecA$55489[esp+808]
00855 d8 05 00 00 00
00 fadd DWORD PTR __real@4@401da09dc9c000000000
0085b 8b 4c 24 04 mov ecx, DWORD PTR _Result$140642[esp+804]
0085f 89 4c 24 14 mov DWORD PTR _vecA$55489[esp+804], ecx
00863 d9 5c 24 08 fstp DWORD PTR _Result$140642[esp+808]
00867 d9 44 24 1c fld DWORD PTR _vecA$55489[esp+812]
0086b d8 05 00 00 00
00 fadd DWORD PTR __real@4@401db213670000000000
00871 8b 54 24 08 mov edx, DWORD PTR _Result$140642[esp+808]
00875 89 54 24 18 mov DWORD PTR _vecA$55489[esp+808], edx
00879 d9 5c 24 0c fstp DWORD PTR _Result$140642[esp+812]
0087d 8b 4c 24 0c mov ecx, DWORD PTR _Result$140642[esp+812]
00881 89 4c 24 1c mov DWORD PTR _vecA$55489[esp+812], ecx
00885 75 b1 jne SHORT $L55491
; 147 : }
; 148 :
; 149 : test(
; 150 : " %f %f %f %f \n"
; 151 : , vecA.x()
; 152 : , vecA.y()
; 153 : , vecA.z()
; 154 : , vecA.w()
; 155 : );
00887 8b 44 24 08 mov eax, DWORD PTR _Result$140642[esp+808]
0088b 8b d1 mov edx, ecx
0088d 8b 4c 24 04 mov ecx, DWORD PTR _Result$140642[esp+804]
00891 52 push edx
00892 8b 54 24 04 mov edx, DWORD PTR _Result$140642[esp+804]
00896 50 push eax
00897 51 push ecx
00898 52 push edx
00899 68 00 00 00 00 push OFFSET FLAT:??_C@_0P@HMKC@?7?$...
0089e dd d8 fstp ST(0)
008a0 e8 00 00 00 00 call ?test@@YAXPBDMMMM@Z ; test
; 156 : }
行列の掛け算が 5 つ連続しています。
計算過程は消去されていますが、
行列の掛け算部分の中間値が残存している様子が確認できます。
中略とした部分には、定数の中間値が出力されていました。
行列の全要素分の、かなりの量の無駄なコードが出力されていました。
検証 5) クランプ
▼ 検証用コード
{
Vec4_t vecB(0,1,2,3);
Vec4_t vecC(1,1,1,1);
Vec4_t vecD(2,2,2,2);
Vec4_t vecA(0,0,0,0);
int i ;
for( i=0 ; i<10000 ; i++ ){
vecA += Clamp( vecB , vecC , vecD );
}
test(
" %f %f %f %f \n"
, vecA.x()
, vecA.y()
, vecA.z()
, vecA.w()
);
}
▼ コンパイル結果
; 161 : {
; 162 : Vec4_t vecB(0,1,2,3);
; 163 : Vec4_t vecC(1,1,1,1);
; 164 : Vec4_t vecD(2,2,2,2);
; 165 : Vec4_t vecA(0,0,0,0);
0016a d9 05 00 00 00
00 fld DWORD PTR __real@4@00000000000000000000
00170 83 c4 14 add esp, 20 ; 00000014H
00173 c7 44 24 14 00
00 00 00 mov DWORD PTR _vecA$55557[esp+36], 0
0017b c7 44 24 18 00
00 00 00 mov DWORD PTR _vecA$55557[esp+40], 0
00183 c7 44 24 1c 00
00 00 00 mov DWORD PTR _vecA$55557[esp+44], 0
0018b b8 10 27 00 00 mov eax, 10000 ; 00002710H
$L55559:
; 166 :
; 167 : int i ;
; 168 : for( i=0 ; i<10000 ; i++ ){
; 169 : vecA += Clamp( vecB , vecC , vecD );
00190 d8 05 00 00 00
00 fadd DWORD PTR __real@4@3fff8000000000000000
00196 48 dec eax
00197 d9 54 24 00 fst DWORD PTR _Result$84659[esp+32]
0019b d9 44 24 14 fld DWORD PTR _vecA$55557[esp+36]
0019f d8 05 00 00 00
00 fadd DWORD PTR __real@4@3fff8000000000000000
001a5 d9 5c 24 04 fstp DWORD PTR _Result$84659[esp+36]
001a9 d9 44 24 18 fld DWORD PTR _vecA$55557[esp+40]
001ad d8 05 00 00 00
00 fadd DWORD PTR __real@4@40008000000000000000
001b3 8b 4c 24 04 mov ecx, DWORD PTR _Result$84659[esp+36]
001b7 89 4c 24 14 mov DWORD PTR _vecA$55557[esp+36], ecx
001bb d9 5c 24 08 fstp DWORD PTR _Result$84659[esp+40]
001bf d9 44 24 1c fld DWORD PTR _vecA$55557[esp+44]
001c3 d8 05 00 00 00
00 fadd DWORD PTR __real@4@40008000000000000000
001c9 8b 54 24 08 mov edx, DWORD PTR _Result$84659[esp+40]
001cd 89 54 24 18 mov DWORD PTR _vecA$55557[esp+40], edx
001d1 d9 5c 24 0c fstp DWORD PTR _Result$84659[esp+44]
001d5 8b 4c 24 0c mov ecx, DWORD PTR _Result$84659[esp+44]
001d9 89 4c 24 1c mov DWORD PTR _vecA$55557[esp+44], ecx
001dd 75 b1 jne SHORT $L55559
; 170 : }
; 171 :
; 172 : test(
; 173 : " %f %f %f %f \n"
; 174 : , vecA.x()
; 175 : , vecA.y()
; 176 : , vecA.z()
; 177 : , vecA.w()
; 178 : );
001df 8b 44 24 08 mov eax, DWORD PTR _Result$84659[esp+40]
001e3 8b d1 mov edx, ecx
001e5 8b 4c 24 04 mov ecx, DWORD PTR _Result$84659[esp+36]
001e9 52 push edx
001ea 8b 54 24 04 mov edx, DWORD PTR _Result$84659[esp+36]
001ee 50 push eax
001ef 51 push ecx
001f0 52 push edx
001f1 68 00 00 00 00 push OFFSET FLAT:??_C@_0P@HMKC@?7?...
001f6 dd d8 fstp ST(0)
001f8 e8 00 00 00 00 call ?test@@YAXPBDMMMM@Z ; test
; 179 : }
vecB , vecC , vecD の存在自体が最適化により消滅し、
結果だけが vecA に足しこまれる様子が確認できます。
内部的には分岐を伴う処理ですが、分岐のコード自体が消滅していました。
-
swizzle 解決を行うコードは消去される
swizzle 解決を行うコードは、最適化により完全に消去されています。
-
計算過程を単純化する最適化が働いている
演算過程で生じている中間値などが、
最適化により消去されています。
分岐なども、最適化により消去されています。
-
定数を計算式で表記可能
定数となるベクトルや行列の値を、
計算式で表記しておいても、
計算式部分はコンパイル段階で解決され、
アセンブリコードレベルでは定数になります。
-
最適化には限度がある
限度を越える冗長な式を書くと、
コンパイラの最適化が追いつかなくなり、
最適化の甘いコードが出力されます。
|