2011年9月24日土曜日

VisualStudio コンパイラの動作について

WindowsでVC++のプログラムの動きを見ていて気づきました。


プログラムが動作するための定数テーブルをバイナリファイルから読み込むプログラムの動きがどうも安定しないので、デバッグしてみました。
当該プログラムはバイナリで「複数」のテーブルデータが格納されており、いったんファイルをすべて読み込み、それをテーブル毎にアドレス計算してmemcopyでテーブル毎に分割して配列に格納しなしてます。
問題はバイナリファイルの定義が構造体の集まりになっており、構造体内の各部のサイズ計算が意図したものとことなり、memcopyでバイナリでコピーしてしまうとバイトがずれているようです。(メモリアクセスする場合は、当該CPU、OS、コンパイラによるアライメント、IA32だと4バイト単位、1IA64やx86_64なんかだと8バイト単位くらいになります。(x86_64は注意が必要です。Linuxだと4バイト単位のアライメントでデフォルトは行われます。OSが32bitか64bitも因子として関係してきますから)

ちょっと実験してみました。プログラムは以下の通りです。

#include <iostream>

// 基本的な構造体
struct {
double w1;
double w2;
} st1;

// 1byteを間に入れてみる
struct {
double w1;
bool dummy; // アライメントを崩す変数を定義する
double w2;
} st2;

void main()
{
std::cout << "Hello World!!" << std::endl;

std::cout << "st1 size = " << sizeof(st1) << std::endl;

std::cout << "st2 size = " << sizeof(st2) << std::endl;

}


これをVS10で設定をデフォルトにて実行した結果を以下に示します。

Hello World!!
st1 size = 16
st2 size = 24

bool変数(必要なのは1bitなんですが、通常のコンパイラは1byteにします)を間に一ついれただけですが、アライメントを調整するため8バイトかけてます。
次にコンパイルの設定で、「コード生成」→「構造体メンバーのアライメント」という項目があり、ここを「規定値」{デフォルトあ8)を「4バイト(/Zp4)」にします。

Hello World!!
st1 size = 16
st2 size = 20

見事にアライメントが4バイトにされ、bool dummyの後には3倍とパディングされました。
VC++ではオプションが「Zp]のようですが、「Zp1」なんてオプションもありました。ちょっと無茶苦茶なコードに(実行時間がかかる)展開されそうですが試してみました。

Hello World!!
st1 size = 16
st2 size = 17

本当に余分なパディングは一切しないようになっています。
やはり高級言語でmemoryのアドレスへの配置を意識しなければいけないようなコードを書くと、維持性で問題を起こします。
Linuxだとこの辺りはCPU、およびOSが32bitか64bitで固定だったと思います。(アライメントは4バイトか8バイトしかない。M$はなんで1バイトのパックまでできるような柔軟性を持たせるんだ!!組み込みのプログラムを書くときしか使わないだろうから、VS-Embededというバージョンを作るなり、そういう属性のプロジェクトをできるようにして、そこでだけ許可するようにしてくれ!)
ちなみにVCには構造体、個々にパックをいくつで(コンパイルを)行うかを指定する#pragma pack指定があり、さらにチューンすることがプログラマに許されています。
(こったプログラムを容易に作れるのと、高い維持性を持たせるのは難しいです)

0 件のコメント:

コメントを投稿