CIでのプログラミングで、GCCを使用して構造体をパックすることが非常に重要であることがわかった場合__attribute__((__packed__))
[...]
あなたが言及しているので__attribute__((__packed__))
、私はあなたの意図はa内のすべてのパディングを排除することだと思いますstruct
(各メンバーに1バイトのアライメントを持たせる)。
すべてのCコンパイラで動作する構造体をパックするための標準はありませんか?
...そして答えは「いいえ」です。構造体(およびスタックまたはヒープ内の構造体の連続配列)に対するパディングとデータのアライメントは、重要な理由で存在します。多くのマシンでは、アライメントされていないメモリアクセスにより、パフォーマンスが大幅に低下する可能性があります(一部の新しいハードウェアでは低下します)。まれなケースでは、メモリアクセスの不整合により、回復不能なバスエラーが発生します(オペレーティングシステム全体がクラッシュすることさえあります)。
C標準は移植性に焦点を当てているため、構造内のすべてのパディングを削除し、任意のフィールドを不整合にするだけの標準的な方法はほとんど意味がありません。
このようなデータをすべてのパディングを排除する方法で外部ソースに出力する最も安全で移植性の高い方法は、単にの生のメモリコンテンツを送信しようとするのではなく、バイトストリームへ/からシリアル化することstructs
です。また、このシリアル化コンテキスト以外でプログラムがパフォーマンスのペナルティを受けることを防ぎ、struct
ソフトウェア全体をスローしてグリッチすることなく、新しいフィールドを自由に追加することもできます。また、エンディアンに対処する余地があり、それが問題になる場合もあります。
コンパイラ固有のディレクティブに到達せずにすべてのパディングを削除する方法が1つありますが、フィールド間の相対的な順序が重要でない場合にのみ適用できます。このようなものを考えると:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
...次のように、これらのフィールドを含む構造体のアドレスに関連するアライメントされたメモリアクセスのためのパディングが必要です。
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... .
はパディングを示します。x
パフォーマンスのために、すべてが8バイトの境界に合わせて調整する必要があります(場合によっては正しい動作にさえなります)。
SoA(配列の構造)表現を使用して、移植可能な方法でパディングを削除できます(8 Foo
つのインスタンスが必要だと仮定しましょう)。
struct Foos
{
double x[8];
char y[8];
};
構造を効果的に破壊しました。この場合、メモリ表現は次のようになります。
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... この:
01234567
yyyyyyyy
...パディングのオーバーヘッドはなくなりました。構造アドレスのオフセットとしてこれらのデータフィールドにアクセスするのではなく、実際には配列であるもののベースアドレスのオフセットとしてアクセスするため、ミスアライメントのメモリアクセスは発生しません。
これには、消費するデータが少ないため(マシンの関連するデータ消費率を低下させるための無関係なパディングがなくなります)、コンパイラーが処理を非常に些細な方法でベクトル化できるため、シーケンシャルアクセスが高速になるというボーナスもあります。
欠点は、コーディングするのがPITAであることです。また、AoSまたはAoSoAの担当者がよりうまく処理できるフィールド間の大きなストライドを使用したランダムアクセスでは、潜在的に効率が低下します。しかし、これは、パディングをなくし、すべてを揃えてねじ込むことなく、できる限り緊密に梱包するための1つの標準的な方法です。