web-dev-qa-db-ja.com

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

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

アライメントが分からないと思います…?

45
user1494506

まあ、「メモリ」は基本的に巨大なバイト配列です。ただし、整数などの最も大きなものは、それらを格納するために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が正しくないことがわかります。構造を試してください。構造体も整列されます(個々のメンバーは最終的に正しいアドレスになる必要があるため)が、そのサイズははるかに大きくなります。

70

2つの演算子は基本的に異なることを行います。 sizeofは型のサイズ(メモリの使用量)を示しますが、alignofは型を整列する必要があるバイト数を示します。テストしたプリミティブに、サイズと同じ配置要件があることは偶然です(考えれば理にかなっています)。

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

_struct Foo {
     int a;
     float b;
     char c;
};
_

alignof(Foo)は4を返します。

17
tskuzzy

古い質問(回答としてマークされていません。)ですが、この例では、Christian Stieberの回答に加えて、違いをより明確にしています。また、sizeof(S)の出力は12ではなく16であるため、Meluhaの回答にはエラーが含まれています。

// c has to occupy 8 bytes so that d (whose size is 8) starts on a 8 bytes boundary
//            | 8 bytes |  | 8 bytes  |    | 8 bytes |
struct Bad  {   char c;      double d;       int i;     }; 
cout << alignof(Bad) << " " << sizeof(Bad) << endl;   // 8 24

//             | 8 bytes |   |   8 bytes    |    
struct Good {   double d;     int i; char c;          };
cout << alignof(Good) << " " << sizeof(Good) << endl; // 8 16

また、他のメンバーはそのメンバーによって制約されているため、最初に最も大きい(この場合はdouble)のサイズでメンバーを並べるのが最適であることも示しています。

12
eolso

提供された回答については、実際のalignmentについて、いくつかの混乱があるようです。 2種類のアラインメントがあるため、混乱が生じる可能性があります。

1。メンバーの配置

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

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バイトであると予想される場合は、メンバーがpackedであると想定し、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番目のパディングメカニズムと密接に関連していますが、アロケーションアライメントmalloc/memallocアロケーション関数のバリアントで指定できます。 std :: aligned_alloc()。したがって、構造体/オブジェクトタイプのバイトアライメントが示唆するものとは異なる(通常は2の倍数が大きい)アライメント境界にオブジェクトを割り当てることができます。

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

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

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

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

  • int posix_memalign(void ** memptr、size_t alignment、size_t size); // POSIX(Linux/UX)
  • void * aligned_alloc(size_t alignment、size_t size); // C++ 11
  • void * std :: aligned_alloc(size_t alignment、size_t size); // c ++ 17
  • void * aligned_malloc(size_t size、size_t alignment); MicrosoftVS2019
12
StarShine

Alignofの値は、基本タイプのsizeofの値と同じです。

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

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

したがって、sizeof値は、特定のデータ型に必要な合計サイズです。 alignof値は、構造内の最大の要素の配置要件です。

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

5
Meluha

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

alignof operator は、指定されたタイプのインスタンスに必要なバイト単位の配置を提供します。

2
Man of One Way

sizeofalignofの違いは何ですか?

どちらも演算子です。どちらもsize_tのタイプを返します。

sizeofは、オブジェクトの「バイト」単位のサイズです。オブジェクトをエンコードするために必要なメモリ領域です。

alignofは、オブジェクトの「バイト」単位のアドレスアライメント要件です。値1は、位置合わせの制限がないことを意味します。 2は、アドレスが偶数アドレスであることを意味します。 4は、アドレスがクワッドアドレスであることを意味します。等.

配置の要件を満たさないオブジェクト参照を試行すると、結果は未定義の動作になります。
例:
。アクセスは機能するかもしれませんが、遅いだけです。
。アクセスしようとすると、プログラムが強制終了される場合があります。

// Assume alignof(int) --> 2
char a[4];   // It is not known that `a` begins on an odd or even address
int *p = a;  // conversion may fail
int d = *p;  // *p is UB.

OPのコードの拡張と出力の例。

  SIZEOF_ALIGNOF(double);
  SIZEOF_ALIGNOF(complex double);
  SIZEOF_ALIGNOF(div_t);
  SIZEOF_ALIGNOF(max_align_t);

8/8
16/8
8/4
32/16