63

sizeof と alignof の違いは何ですか?

#include <iostream>

#define SIZEOF_ALIGNOF(T) std::cout<< sizeof(T) << '/' << alignof(T) << std::endl

int main(int, char**)
{
        SIZEOF_ALIGNOF(unsigned char);
        SIZEOF_ALIGNOF(char);
        SIZEOF_ALIGNOF(unsigned short int);
        SIZEOF_ALIGNOF(short int);
        SIZEOF_ALIGNOF(unsigned int);
        SIZEOF_ALIGNOF(int);
        SIZEOF_ALIGNOF(float);
        SIZEOF_ALIGNOF(unsigned long int);
        SIZEOF_ALIGNOF(long int);
        SIZEOF_ALIGNOF(unsigned long long int);
        SIZEOF_ALIGNOF(long long int);
        SIZEOF_ALIGNOF(double);
}

出力します

1/1 1/1 2/2 2/2 4/4 4/4 4/4 4/4 4/4 8/8 8/8 8/8

私は、アライメントが何であるかを理解していないと思います...?

4

8 に答える 8

81

ええと、「メモリ」は基本的にバイトの巨大な配列です。ただし、整数などのほとんどの大きなものは、それらを格納するために1バイト以上を必要とします。たとえば、32ビット値は4バイトの連続したメモリを使用します。

現在、コンピュータのメモリモジュールは通常「バイト」ではありません。また、4バイトのブロックのように、「並列」に数バイトで編成されます。

CPUの場合、整数のようなものを読み取るときに、このようなブロック境界を「越えない」方がはるかに簡単=効率的=パフォーマンスが向上します。

memory byte    0 1 2 3     4 5 6 7       8 9 10 11
 integer       goooood
                   baaaaaaaaad

これが「アライメント」の意味です。4のアライメントは、このタイプのデータを4の倍数のアドレスから開始して保存する必要がある(またはCPUに依存する必要がある)ことを意味します。

sizeof==alignofが正しくないことがわかります。構造を試してください。構造も整列されますが(個々のメンバーが正しいアドレスに配置される必要があるため)、サイズははるかに大きくなります。

于 2012-07-08T21:48:52.003 に答える
24

提供された回答については、実際のアライメントとは何かについて混乱があるようです。アラインメントには 2 種類あるため、おそらく混乱が生じるでしょう。

1. メンバーの調整

これは、構造体/クラス型のメンバーの特定の順序について、インスタンスがバイト数でどのくらい大きいかを示す定性的な尺度です。一般に、コンパイラは、メンバーが構造内でバイト サイズの降順 (つまり、最初に最大、最小のメンバーが最後) に並べられている場合、構造/クラス インスタンスを圧縮できます。検討:

struct A
{
  char c; float f; short s;
};

struct B
{
  float f; short s; char c;
};

どちらの構造にもまったく同じ情報が含まれています。この例のために; float 型は 4 バイト、short 型は 2 バイト、文字は 1 バイトです。ただし、最初の構造体 A はランダムな順序でメンバーを持ち、2 番目の構造体 B はバイト サイズに従ってメンバーを並べ替えます (これは特定のアーキテクチャでは異なる場合があります。この例では 4 バイト アラインメントの x86 Intel CPU アーキテクチャを想定しています)。次に、構造体のサイズを検討してください。

printf("size of A: %d", sizeof (A)); // size of A: 12;
printf("size of B: %d", sizeof (B)); // size of B: 8;

サイズが 7 バイトであると予想される場合は、メンバーが1 バイトのアラインメントを使用して構造体にパックされていると想定することになります。これを許可するコンパイラもありますが、一般に、ほとんどのコンパイラは歴史的な理由から 4 バイトまたは 8 バイトのアラインメントを使用します (ほとんどの CPU は DWORD (ダブルワード) または QWORD (クワッドワード) 汎用レジスタで動作します)。

パッキングを実現するために、2 つのパディング メカニズムが機能しています。

  1. 最初に、結果のバイト サイズがバイト アラインメントより小さいか等しい場合、バイト アラインメントより小さいバイト サイズを持つ各メンバーは、次のメンバーと「マージ」されます。構造体 B では、メンバー s と c をこの方法でマージできます。それらを合わせたサイズは、s の場合は 2 バイト + c の場合は 1 バイト == 3 バイト <= 4 バイトのアラインメントです。構造体 A の場合、このようなマージは発生せず、各メンバーは構造体のパッキングで実質的に 4 バイトを消費します。

  2. 次の構造体がアラインメント境界で開始できるように、構造体の合計サイズが再度パディングされます。例 B では、合計バイト数は 7 になります。次の 4 バイト境界はバイト 8 にあるため、構造体は 1 バイトでパディングされ、インスタンスのタイトなシーケンスとして配列割り当てが可能になります。

Visual C++ / GCC では、1 バイト、2 バイト、およびそれ以上の 2 バイトの倍数の異なるアラインメントが許可されていることに注意してください。これは、アーキテクチャーに最適なコードを生成するコンパイラーの能力に反することを理解してください。実際、次の例では、読み取り操作ごとに 1 バイト命令を使用して、各バイトが 1 バイトとして読み取られます。実際には、4 バイトが同じ DWORD にあり、1 つの命令で CPU レジスタにロードできる場合でも、ハードウェアはキャッシュに読み取られた各バイトを含むメモリ ライン全体をフェッチし、命令を 4 回実行します。

#pragma pack(push,1)
struct Bad
{
  char a,b,c,d;
};
#pragma pack(pop)

2. 割り当ての調整

これは、前のセクションで説明した 2 番目のパディング メカニズムと密接に関連していますが、割り当てアラインメントは、 std::aligned_alloc()などのmalloc() / memalloc()割り当て関数のバリアントで指定できます。したがって、構造体/オブジェクト型のバイト アラインメントが示唆するものとは異なる (通常は 2 の倍数より大きい) アラインメント境界でオブジェクトを割り当てることができます。

size_t blockAlignment = 4*1024;  // 4K page block alignment
void* block = std::aligned_alloc(blockAlignment, sizeof(T) * count);

このコードは、タイプ T の count インスタンスのブロックを、4096 の倍数で終わるアドレスに配置します。

このような割り当てアラインメントを使用する理由も、純粋にアーキテクチャ上のものです。たとえば、アドレスの範囲がキャッシュ レイヤーにうまく収まるため、ページ アラインされたアドレスからのブロックの読み取りと書き込みが高速になります。異なる「ページ」に分割された範囲は、ページの境界を越えるときにキャッシュを破棄します。メディア (バス アーキテクチャ) が異なれば、アクセス パターンも異なります。一般に、4、16、32、および 64 K ページ サイズのアラインメントは珍しくありません。

言語バージョンとプラットフォームは、通常、このような整列された割り当て関数の特定のバリアントを提供することに注意してください。たとえば、Unix/Linux 互換のposix_memalign()関数は、ptr 引数によってメモリを返し、失敗した場合はゼロ以外のエラー値を返します。

  • int posix_memalign(void **memptr, size_t アライメント, size_t サイズ); // POSIX(Linux/UX)
  • void *aligned_alloc( size_t アライメント, size_t サイズ ); // C++11
  • void *std::aligned_alloc( size_t アライメント, size_t サイズ ); // c++17
  • void *aligned_malloc( size_t サイズ, size_t アライメント ); MicrosoftVS2019
于 2016-12-01T11:14:13.883 に答える
19

2つの演算子は根本的に異なることを行います。sizeofタイプのサイズ(必要なメモリ量)alignofを示し、タイプを整列する必要のあるバイト数を示します。テストしたプリミティブには、サイズと同じアライメント要件があります(これについて考えると理にかなっています)。

代わりに構造体がある場合はどうなるか考えてみてください。

struct Foo {
     int a;
     float b;
     char c;
};

alignof(Foo)4を返します。

于 2012-07-08T21:45:31.110 に答える
5

alignof の値は、基本型の sizeof の値と同じです。

違いは、構造体の使用など、使用される定義済みデータ型にあります。例えば

typedef struct { int a; double b; } S;
//cout<<alignof(s);                              outputp: 8;
//cout<<sizeof(S);                               output: 12;

したがって、sizeof 値は、指定されたデータ型に必要な合計サイズです。alignof value は、構造体の最大要素の位置合わせ要件です。

alignof の使用: 特定のアラインメント境界にメモリを割り当てます。

于 2012-08-28T07:24:21.093 に答える
2

このsizeof演算子は、実際の型または型のインスタンスのサイズをバイト単位で示します。

このalignof演算子は、指定された型のインスタンスに必要なアライメントをバイト単位で提供します。

于 2012-07-08T21:43:16.273 に答える