コンパイラーが生成するコードにはデータ構造の実際の知識がないこと(アセンブリーレベルには存在しないため)とオプティマイザーも認識しないことが重要です。コンパイラは、データ構造ではなく、各関数のコードのみを生成します。
わかりました、それも定数データセクションなどを書き込みます。
これに基づいて、オプティマイザーはデータ構造を出力しないため、メンバーを「削除」または「削除」しないと既に言うことができます。メンバーを使用する場合と使用しない場合があるコードを出力します。その目的の1つは、メンバーの無意味な使用(つまり、書き込み/読み取り)を排除することでメモリまたはサイクルを節約することです。
その要点は、「コンパイラーが関数の範囲内(インライン化された関数を含む)で、未使用のメンバーが関数の動作(および関数が返すもの)に違いがないことを証明できる場合、その可能性はメンバーの存在はオーバーヘッドを引き起こしません。」
関数と外界との相互作用をコンパイラーにとってより複雑/不明確にする(より複雑なデータ構造を取得/返すなど) std::vector<Foo>
、別のコンパイル単位で関数の定義を非表示にする、インライン化を禁止/無効化するなど)。 、使用されていないメンバーが効果がないことをコンパイラーが証明できない可能性が高まっています。
コンパイラーが行う最適化にすべて依存するため、ここには難しいルールはありませんが、(YSCの回答に示されているように)些細なことを行う限り、複雑なこと(たとえば、std::vector<Foo>
インライン化するには大きすぎる関数からは)おそらくオーバーヘッドが発生します。
ポイントを説明するために、次の例を検討してください。
struct Foo {
int var1 = 3;
int var2 = 4;
int var3 = 5;
};
int test()
{
Foo foo;
std::array<char, sizeof(Foo)> arr;
std::memcpy(&arr, &foo, sizeof(Foo));
return arr[0] + arr[4];
}
ここでは重要なこと(アドレスの取得、バイト表現からのバイトの検査と追加)を行いますが、オプティマイザはこのプラットフォームで結果が常に同じであることを理解できます。
test(): # @test()
mov eax, 7
ret
のメンバーはFoo
メモリを占有しなかっただけでなく、Foo
存在することすらありませんでした!最適化できない他の使用法がある場合、たとえば、sizeof(Foo)
問題になる可能性がありますが、コードのそのセグメントに対してのみです!このようにすべての使用法を最適化できる場合、たとえばの存在はvar3
生成コードに影響しません。しかし、それが別の場所で使用されている場合でも、test()
最適化されたままになります!
つまり、の各使用法Foo
は個別に最適化されます。不要なメンバーが原因でより多くのメモリを使用する場合もあれば、使用しない場合もあります。詳細については、コンパイラのマニュアルを参照してください。