GAIO CLUB

2023年09月07日

【第12回】プログラムサイズ

静的解析/コンパイラ技術
いまさら聞けない静的解析/コンパイラ技術
これまで3回にわたってプログラムとメモリの関係についてお話してきました。
今回は、プログラムがどれくらいのメモリを使用するかを調べる方法についてお話しします。

コードサイズとデータサイズ

コンパイル&リンクしたバイナリファイルのコードサイズ(総関数サイズ)とデータサイズ(総静的変数サイズ)は、リンカの生成するリンクマップを見れば分かります。
リンクマップの形式はツールメーカ毎に固有ですが、その内容はマニュアルで確認できると思います。
ここでは、GAIO製リンカが出力したリンクマップの一部をお見せします。
分かりやすくするためにサイズゼロのセクション情報はグレーアウトしています。
日本語の説明書きのとおり、青色は初期化変数なので、StartUpが紫色の初期値をコピーします。緑色は非初期化変数なので、StartUpがゼロクリアします。赤字が関数と定数で、紫字は初期値です。
の各先頭アドレスはリンクパラメタで与えたものです。
赤字部0x00600109から0x0060010Cの間の2バイトはアラインメント調整のための空きです。
文字では分かりにくいので、マップ図にしてみました。リンクマップ説明の色分けに合わせています。
コード(ROM)とデータ(RAM)の様子が分かりやすくなったと思います。
このプログラムのコードサイズは684(0x2AC)バイト、データサイズは804(0x324)バイトです。

スタックサイズ

関数毎のスタックフレームはプログラムが動きながらSPをずらして確保するので、リンクマップには現れません。スタックフレームサイズはCコンパイラが知っています。
以前のCコンパイラは生成したアセンブリコード内にコメントでスタック消費量情報を出すものが多く、GAIO製コンパイラも同じでした。最近はもっと便利な情報があるかもしれません。

下のリストは、GAIO製コンパイラの生成したアセンブリコードからスタックフレームサイズ情報のコメント行を抜き出したものです。
;main   stack size : 8
;Sub_A	stack size : 24
;Sub_A1	stack size : 32
;Sub_A2	stack size : 40
;Sub_B	stack size : 48
;Sub_C	stack size : 52
;Sub_C1	stack size : 60
;Sub_X	stack size : 16
スタックフレームは関数が動いている時に使用し、関数がリターンした時には解放されます。
ですから、スタックの必要サイズは、関数のコールツリーを考慮しなくてはなりません。コールツリーの末端にある関数が動く時にトータルでどれくらいのスタックフレームサイズが必要なのかを求める必要があります。
そして、各末端関数動作時のスタックフレームサイズ合計値の一番大きな値が、必要なスタックサイズです。
上でお見せした関数をコールツリーにして、各関数のスタックフレームサイズを書き込みました。
このプログラムでは、main→Sub_C→Sub_C1→Sub_Xの呼び出しで最大のスタックを消費し、サイズは136バイトです。
なお、この例では使用していませんが、スタック計測のコールツリーには、Cコンパイラの標準ライブラリや演算処理ライブラリも含めなければなりません。もし、割り込み処理が同じスタックを使用するなら、それらも考慮する必要があります。

ヒープサイズ

前回お話しした通り、ヒープサイズを正確に計測するのは困難です。
mallocのメモリ管理仕様に依存し、free処理のタイミングにも影響を受けます。ヒープサイズにはmalloc管理情報も含まれるので、断片化の影響も受けます。十分なエリアがあるかの確認には、上限を決めてランニングテストを行い、メモリ不足にならないことを確認するしかないかもしれません。

Linuxでの計測

GAIOツールによる計測の話をしてきましたが、LinuxでGCC/ARMコンパイラを使用する場合は、もっと簡単にサイズの確認ができるのでご紹介しておきます。ARM以外でもGCCであれば可能と思います。
コードサイズとデータサイズについては、リンク後のファイルに対して sizeコマンドを使用することで確認できます。さきほど使用したサンプルソースで試した結果は以下の通りです。
$ arm-none-eabi-gcc -I/usr/arm-none-eabi/include -L/usr/arm-none-eabi /lib -o Stack Stack.c
$ size Stack
  text	   data	    bss	    dec	    hex	   filename
  2532	   1092	    28	   3652	    e44    Stack
sizeコマンドが出力する情報は、textの情報が、関数と定数等のROMに関わるサイズ、dataは初期化変数サイズ、bssは非初期化変数サイズを示します。decとhexは合計サイズを10進と16進数で示しています。

スタック使用量は、コンパイル時に -fstack-usageを指定するとファイル名.suというテキストファイルに情報が書き出されます。
$ arm-none-eabi-gcc -I/usr/arm-none-eabi/include -c -fstack-usage Stack.c
$ cat Stack.su
Stack.c:9:6:main	16	static
Stack.c:16:6:Sub_A	32	static
Stack.c:22:6:Sub_A1	40	static
Stack.c:28:6:Sub_A2	48	static
Stack.c:34:6:Sub_B	56	static
Stack.c:40:6:Sub_C	56	static
Stack.c:46:6:Sub_C1	72	static
Stack.c:52:6:Sub_X	32	static
各行の情報は、ファイル名行番号カラム番号関数名、スタックサイズ、です。staticの表示は、関数の入り口で確保するサイズを示し、dynamicと表示されることもあります。dynamicは関数の先頭だけでなく、関数処理途中で確保するエリアもあることを示します。

この記事を読んだ人はこんなページを読んでいます。

筆者紹介

浅野 昌尚(あさの まさなお)

ガイオ・テクノロジー株式会社

開発2部 QTXグループ

1980年代から30年以上にわたり汎用構造のCコンパイラ開発に従事し、その間に8ビットマイコンからRISC・VLIW・画像処理プロセッサまで、さまざまなCPU向けのクロスCコンパイラを開発。

人気のコラム

最新のコラム