Важливо усвідомити, що код, який створює компілятор, не має фактичних знань про ваші структури даних (оскільки такого не існує на рівні збірки), як і оптимізатора. Компілятор виробляє лише код для кожної функції , а не структури даних .
Гаразд, він також пише постійні розділи даних тощо.
Виходячи з цього, ми вже можемо сказати, що оптимізатор не буде "видаляти" або "виключати" члени, оскільки він не виводить структури даних. Він виводить код , який може або не повинен використовувати члени, і серед його цілей є економія пам'яті або циклів шляхом усунення безглуздого використання (тобто запису / читання) членів.
Суть цього полягає в тому, що "якщо компілятор може довести в межах функції (включаючи функції, які були вкладені в неї), що невикористаний член не має різниці в тому, як функція функціонує (і що вона повертає), тоді великі шанси, що присутність члена не спричиняє накладних витрат ".
По мірі того, як ви робите взаємодію функції із зовнішнім світом для компілятора більш складною / незрозумілою (беруть / повертають більш складні структури даних, наприклад std::vector<Foo>
, приховують визначення функції в іншому блоці компіляції, забороняють / знеохочують вбудовування тощо). , стає все більш імовірним, що компілятор не може довести, що невикористаний член не має ефекту.
Тут немає жорстких правил, оскільки все залежить від оптимізацій, які робить компілятор, але поки ви робите тривіальні речі (як показано у відповіді YSC), дуже ймовірно, що ніяких накладних витрат не буде, тоді як робити складні речі (наприклад, повернення a 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
оптимізується незалежно. Хтось може використовувати більше пам'яті через непотрібного члена, хтось може ні. Зверніться до посібника компілятора для отримання детальної інформації.