ページ 1 / 1
アラインメント指定
Posted: 2006年4月01日(土) 00:37
by イグトランス
このコードでは16と出力されます。
コード: 全て選択
#strict
#prompt
Type Align(8) S1
x As DWord
y As DWord
End Type
Print SizeOf (S1)
しかしそれと同等と私が思った次のVC++のコードでは8が出力されます。
コード: 全て選択
#include <iostream>
#include <windows.h>
__declspec(align(8)) struct S1
{
DWORD x;
DWORD y;
};
int main()
{
std::cout << sizeof (S1) << std::endl;
}
色々と試してみると,次のコードでは16と出力されました。
コード: 全て選択
#include <iostream>
#include <windows.h>
struct S1
{
__declspec(align(8)) DWORD x;
__declspec(align(8)) DWORD y;
};
int main()
{
std::cout << sizeof (S1) << std::endl;
}
つまりアラインメントの対象が構造体全体であるか,個々のメンバであるかの違いのようです。
私はABのTypeに指定するAlignは構造体全体に対するアラインメントの指定であるべきだと思います。
つまり最初のABのコードは前者のVC++のコードに対応するようにするということです。
そもそもWinAPIで使われる構造体の中には8バイトアラインメントが指定されているものがあります。
そのような構造体をABに持ってくるとき,試しにType Align(8)としてみたことからこのことに気づきました。
VC++ではalign(8)と指定されているのに,ABでは付けるとエラーになるので省くということは,
どうも違和感を感じさせます。それが前者の意味にしてほしいと言う理由ですね。
Re: アラインメント指定
Posted: 2006年4月01日(土) 13:24
by NoWest
この件につきましては、私のコラム執筆にあたり、山本様とメールで
いくつかやりとりがありましたので、回答させて頂きます。
まず、ActiveBasicのアライメントについて抜粋
NoWest さんが書きました:VC++のアライメントの初期値は8byte、
VB、BC++のアライメントは4byteなんですが
ABって何Byteなんでしょうか?
山本 さんが書きました:アラインメントに初期値はありません。
この説明は、非常に重要なポイントなので、よく理解してください。
そもそも、アラインメントとは、構造体の先頭を0としたときに、メンバ変数の
データサイズでそのオフセットが割り切れない場合に、発生するものです。パ
ディングを挿入して、そのメンバのデータサイズで割り切れるようにする働きが
あるんですね。
簡単にいうと「ActiveBasicはそもそもアライメントと呼ばれる概念が希薄で
APIで使用される構造体が問題なく動くように自動的に調節する。」ってことだそうです。
つまり、イグトランス様が試された
コード: 全て選択
Type Align(8) S1
x As DWord
y As DWord
End Type
というコードはそもそもAlign(8)を付けなくてもActiveBasicでは問題なく
動くと言うことです。
結局Alignとは何なのか?ってことについて抜粋
山本 さんが書きました:構造体のアラインメントについては、コンパイラによって互換性が薄いようで
す。CPUの処理速度に関係してくる内容なので、ターゲットマシンやOSによって
仕様が異なるという意味では納得はできますが、、、作っているこちらもややこ
しい限りですf(^^
・自動調節(デフォルト)
・互換性を重視するため、1バイトまたは2バイトでアラインメントをとる
これら以外に関しては、必要なケースを見たことがありませんので、4~16バイ
トアラインメントに関して、何か問題が生じるようであれば、VCのものと比較し
ながらサポートを行おうと思います。
という返答を頂いております。
私も、最初は混乱しましたので、意見としてはイグトランス様に賛成です。
Posted: 2006年4月04日(火) 23:54
by イグトランス
最初にAlign(8)と付けてみたのはWin64への移植性が心配になったからです。
幸いにもx86系のCPUもアラインメントの扱いがなっていなくても動いてしまいますが,だからこそ心配になったのです。
そもそもNバイトにアラインメント(境界調整)するということは,
メモリアドレスがNの倍数になる位置に配置するということのはずです。
それで気づいたのですが,これは0と出力されなくてはいけないはずなのですが,そうではありませんでした。
(私がコンパイルすると4が出力されました。)
コード: 全て選択
#strict
#prompt
Type Align(8) S1
x As DWord
y As DWord
End Type
Dim dw As DWord
Dim s1 As S1
Dim address As ULONG_PTR
Print (VarPtr(s1) As ULONG_PTR) Mod 8
そしてこれと(メンバの配置以外は)ほぼ等価なはずのVC++のコードです。
コード: 全て選択
#include <iostream>
#include <windows.h>
__declspec(align(8)) struct S1
{
DWORD x;
DWORD y;
};
int main()
{
using namespace std;
DWORD dw;
S1 s1;
cout << reinterpret_cast<ULONG_PTR>(&s1) % 8 << endl;
return 0;
}
Posted: 2006年4月05日(水) 00:47
by 山本
アラインメント、一番頭が痛くなる問題ですね。アプリが強制終了して、原因を突き詰めたらデータの境界ラインが8の倍数になってなかったとか…。64ビットコンパイラの製作場面では、しょっちゅう遭遇していました。
では本題。
私はABのTypeに指定するAlignは構造体全体に対するアラインメントの指定であるべきだと思います。
つまり最初のABのコードは前者のVC++のコードに対応するようにするということです。
そもそもWinAPIで使われる構造体の中には8バイトアラインメントが指定されているものがあります。
そのような構造体をABに持ってくるとき,試しにType Align(8)としてみたことからこのことに気づきました。
VC++ではalign(8)と指定されているのに,ABでは付けるとエラーになるので省くということは,
どうも違和感を感じさせます。それが前者の意味にしてほしいと言う理由ですね。
指定したアラインメント値よりも小さなデータ型が存在するとき、現状のABの仕様ではムリヤリにアラインメントをとっているのが現状です。
私のほうでも、VC++での動作の違いを確認できました。移植のことを考えると、あわせたほうが無難ですね。アラインメント誤差によるバグは一番やっかいなものであるのはご承知のとおりですので、改善しようと思います。
最初にAlign(8)と付けてみたのはWin64への移植性が心配になったからです。
幸いにもx86系のCPUもアラインメントの扱いがなっていなくても動いてしまいますが,だからこそ心配になったのです。
そもそもNバイトにアラインメント(境界調整)するということは,
メモリアドレスがNの倍数になる位置に配置するということのはずです。
それで気づいたのですが,これは0と出力されなくてはいけないはずなのですが,そうではありませんでした。
(私がコンパイルすると4が出力されました。)
鋭いポイントをつかれますねf(^^
確かに、Win64では、8バイト区切りのデータ配置を機械語が要求してきます(違反すると、強制終了になります)。よって、Win64の環境下では、各変数の先頭アドレスは8で割り切れる数値となっています。これは、Win32の環境とは異なる点です。
このように、現状の64ビット版ActiveBasicでは8バイト境界ラインを意識した仕様になっておりますので、ご安心ください。