GCC-4.6の stack-usageを使ってみた
GCC 4.6 RCが 3月14日に出ました。なんでもうすぐ正式版も出るかと
思います。Changlogを見ていると気になる機能がありました。それが
stack-usage. 関数ごとのスタックの使用量が確認できるというものです。
これは Adaだとだいぶ前から実装されていたんですけど、4.6からは
どの言語からでも利用することができます。
GCC4.6のインストール
GCC 4.5.1をビルドする - Life is very short
GCC 4.5.2をビルドする - Life is very short
と同じなので、これらの記事を参照してください。
準備さえできれば以下のコマンドで作成できます。
% tar xf archives/gcc-4.6.0-RC-20110314.tar.bz2 % cd gcc-4.6.0-RC-20110314 % mkdir build ; cd build % ../configure --enable-languages=c --with-gmp=/usr/local \ --with-mpfr=/usr/local --with-mpc=/usr/local --with-ppl=/usr/local \ --with-cloog=/usr/local --prefix=/home/syohei/local/gcc-4.6.0 \ --disable-nls --disable-multilib % make % make install % export PATH=/home/syohei/local/gcc-4.6.0/bin:$PATH
試す
以下のコードで試します。何の意味もないコードですが、最適化を
ごまかすためにやってます。
#include <stdio.h> #include <stdlib.h> static int func(int a) { int array[1024]; int i, sum = 0; for (i = 0; i < a; i++) { sum += array[i]; } return sum; } int main (int argc, char *argv[]) { int a; if (argc >= 2) { a = atoi(argv[1]); } else { a = 1024; } return func(a); }
コンパイルします。
% gcc -O2 -fstack-usage stack_usage.c
するとカレントディレクトリに (ソース名).suというファイルが生成
されます。このファイルにスタック情報が書かれています。内容は以下のとおりです。
stack_usage.c:4:12:func 3984 static stack_usage.c:16:5:main 16 static
まだ読み方があまりわかっていないのですが、funcという関数は静的に
3984バイト割り当てられており、mainは静的に 16バイト割り当てられて
いることがわかります。最適化をかけたせいで 4096ではないですね。
ファイル名のあとフィールドですが、一つ目は行数なんだけど、二つ目は
関数のカラムだそうです。(@koieさん情報ありがとうございます。)
ドキュメント
http://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options
にありますが、まだデベロップメントフェーズなので、そのうちちゃんと
書かれるのではないかと思います。
おまけ1
本当は Linuxカーネルで試してみたかったんだけど、makeに失敗して
しまったので、Perl-5.13.10のビルド時に -fstack-usageをつけてみた。
% sh Configure -Dcc=gcc -Doptimize='-O3 -pipe -fstack-usage' \ -Dusedevel -des % make
スタック使用量の多いものを見てみると以下のような感じでした。
% cat **/*.su | sort -n -r -k2 |head -n10 Cwd.c:521:1:XS_Cwd_abs_path 16640 static bsd_glob.c:508:1:glob0 16464 static sv.c:7492:1:Perl_sv_gets 8464 static bsd_glob.c:722:1:4 8352 static bsd_glob.c:328:1:globexp2 8288 static bsd_glob.c:220:1:bsd_glob 8240 static huffman.c:63:6:BZ2_hbMakeCodeLengths 5264 static blocksort.c:751:6:1 4800 static pp_sys.c:1577:1:Perl_pp_sysread 4288 static util.c:3385:1:Perl_find_script 4224 static
関数名がちゃんと出ないのがある。
bsd_glob.c(File::Glob)の4っていうのは glob3で
blocksort.c(Compress::Raw::Bzip2)の1っていうのは mainSort関数です。
ソースを見ると大抵 MAXPATHLENの長さの配列の確保が要因のようですね。
testgen
testgeテストスイートの結果は以下の通り
=== gcc Summary === # of expected passes 63844 # of unexpected failures 12 WARNING: gcc_version failed: Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/home/syohei/local/gcc-4.6.0/libexec/gcc/x86_64-unknown-linu x-gnu/4.6.0/lto-wrapper Target: x86_64-unknown-linux-gnu Configured with: ../configure --enable-languages=c --with-gmp=/usr/local --with- mpfr=/usr/local --with-mpc=/usr/local --with-ppl=/usr/local --with-cloog=/usr/lo cal --prefix=/home/syohei/local/gcc-4.6.0 --disable-nls --disable-multilib Thread model: posix gcc version 4.6.0 20110314 (prerelease) (GCC)
まとめ
GCC 4.6の stack-usageについて紹介しました。組み込みではメモリはこんな
時代でも貴重な資源なので、スタック使用量というのは重要なものです。
なんでちゃんと計測を行う必要があります。それが少し楽になったという
ことですね。
課題としては、どのコールフローのときにスタックの使用量が最大になるかと
いうことです。その辺も自動でやってくれればいいのですが、今は関数レベル
なのでその辺はユーザが工夫する必要がありそうです。コールグラフ生成
ツール等と組み合わせればなんとかなるのではないかと思いますので、
そのあたりを調査しようかと思ってます。
(追記)dynamicに割り当てられる場合
dynamicにスタックを割り当てるってなんだって思って少し調べてみました。
C99だと配列のサイズに変数が指定できるからそれを使えば dynamicになり
ました。サンプルは以下のとおりです。
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { int n, sum = 0; if (argc >= 2) { n = atoi(argv[1]); } else { n = 1024; } int a[n]; for (int i = 0; i < n; i++) { sum += a[i]; } printf("sum = %d\n", sum); return 0; }
% gcc -std=c99 -fstack-usage stack_dynamic.c
得られた stack_dynamic.suを見ると以下のようになりました。
stack_dynamic.c:4:5:main 112 dynamic
112というのはどこから出た数字なんだろう?というよりこのようなコードの
場合、どうやってスタックの処理をしているんだろう?そのあたりの勉強が
必要そうですね。