Entry

プログラミングメモ - 静的な配列の初期化の話(補遺)

2008年10月16日

前回のエントリでは,VC と gcc が作るアセンブリを見てみたんですけど,memset(3) を使いつつ最適化オプションを高めに設定した場合については,インライン展開するかもなぁ……とか書いただけだったのでした。つことで,ちょっと見てみた。

例によって,下のようなコードで試してみます。

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
   char a[10000];
   int i;

   memset(a, 0, 10000);
   for (i = 0; i < 10; i++) {
       printf("%d ", a[i]);
   }
   putchar('\n');

   return 0;
}

"= {0};" で初期化した場合の結果は,前回のエントリを参照していただくとして,ここでは VC6 の最適化オプションを有効にしたときのアセンブリを見てみることにします。

まず,最適化しない場合

   push     10000
   push     0
   lea      eax, DWORD PTR _a$[ebp]
   push     eax
   call     _memset
   add      esp, 12

ちゃんと memset(3) を呼んでいます。直接関係ないんですけど,アセンブリで関数呼び出すには,スタックに引数をお尻から積んで(push して)いって,最後に関数名を call することで行います。ここでは memset(a, 0, 10000); と呼び出しているので,お尻から 10000, 0, _a$[ebp](a の先頭アドレス)と push していって,最後に _memset を call しています。これは定石どおり。一般に,memset(3) を呼ぶと遅くなるってのは,ここで引数を push したり,スタックポインタを動かしたりとかいった関数呼び出しの手続きに時間がかかるからです。

じゃあ,最適化するとどうななるのか。ここでは /O2 の最適化オプションを付けてコンパイルしてみました。

   mov      ecx, 2500
   xor      eax, eax
   lea      edi, DWORD PTR _a$[esp+10008]
   xor      esi, esi
   rep stosd

memset(3) 呼ばれませんね。

やってることは,前回 "= {0};" で定義(と初期化)していたときとほとんど同じです。違いは,こちらが要素の頭を初期化していないだけ。ここでは初期化コードを書いてないので当たり前ですね。

一方,本題の memset(3) を call しなくなった原因は,おそらく,memset(3) の第2・第3引数がイミディエイト(即値)だったからだと思います。最適化の過程で関数の内容を展開してしまった,と。

でまぁ,不毛な比較なんですけど,"= {0};" で初期化する場合と今回の場合を比較すると,memset(3) を使った方が,要素の先頭を初期化しない分だけ若干速い気がします(少なくとも理屈の上では)。"= {0};" の場合は,命令をふたつに分けて,しかも先頭要素を mov 命令で素朴にコピーしていたわけで,stosd でいっぺんに転送するよりは多分遅い。まぁ,不毛なほど「若干」なんでしょうが。

前回は,"= {0};" で初期化した場合も,場合によっては memset(3) が呼ばれてしまうってな話があったわけですけれど,memset(3) を書いた場合も,場合によってはインラインで展開されてしまう,と。こりゃほんとに好みの問題なんだろうなぁ……きっと。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN