Чи швидше x + =, ніж x = x + a?


84

Я читав "Страуструпа" "Мова програмування C ++", де він каже, що з двох способів додати щось до змінної

x = x + a;

і

x += a;

Він віддає перевагу +=тому, що це, швидше за все, краще реалізувати. Я думаю, він має на увазі, що це теж працює швидше.
Але чи справді це? Якщо це залежить від компілятора та інших речей, як мені перевірити?


45
"Мова програмування C ++" була вперше опублікована в 1985 р. Остання версія була опублікована в 1997 р., А спеціальне видання версії 1997 р. Було опубліковано в 2000 р. Як наслідок, деякі частини дуже застаріли.
JoeG

5
Дві лінії потенційно можуть зробити щось зовсім інше. Ви повинні бути більш конкретними.
Kerrek SB


26
Сучасні компілятори досить розумні, щоб ці питання можна було вважати «застарілими».
gd1

2
Повторно відкрив це, оскільки дублікат запитання задає про C, а не C ++.
Kev

Відповіді:


212

Будь-компілятор варто його солі буде генерувати точно такій же послідовності на машинній мові для обох конструкцій для будь-якого вбудованого типу ( int, float, і т.д.) до тих пір , як твердження дійсно так просто , як x = x + a; і оптимізація включена . (Зокрема, GCC -O0, який є типовим режимом, виконує анти-оптимізацію , наприклад, вставляє в пам’ять абсолютно непотрібні сховища, щоб гарантувати, що налагоджувачі завжди можуть знаходити змінні значення.)

Якщо твердження є більш складним, вони можуть бути різними. Припустимо f, це функція, яка повертає покажчик

*f() += a;

дзвонить fлише один раз, тоді як

*f() = *f() + a;

називає це двічі. Якщо fє побічні ефекти, один із двох буде помилковим (можливо, останній). Навіть якщо fвін не має побічних ефектів, компілятор може не змогти усунути другий виклик, тому останній дійсно може бути повільнішим.

І оскільки ми тут говоримо про С ++, то для типів класів, які перевантажують operator+і operator+=. Якщо xце такий тип, то - перед оптимізацією - x += aозначає

x.operator+=(a);

тоді як x = x + aперекладається на

auto TEMP(x.operator+(a));
x.operator=(TEMP);

Тепер, якщо клас написаний належним чином, а оптимізатор компілятора достатньо хороший, обидва завершать генерування однієї і тієї ж машинної мови, але це не зовсім впевнене, як для вбудованих типів. Ймовірно, саме про це думає Строуструп, коли він заохочує використовувати +=.


14
Також є ще один аспект - читабельність . Ідіома C ++ для додавання exprдо varis var+=exprта написання його в інший спосіб буде бентежити читачів.
Тадеуш Копець

21
Якщо ви виявите, що пишете *f() = *f() + a;, можливо, ви захочете добре поглянути на те, чого ви насправді намагаєтесь досягти ...
Адам Девіс,

3
І якщо var = var + expr бентежить вас, але var + = expr ні, ви найдивніший інженер програмного забезпечення, якого я коли-небудь зустрічав. Або читаються; просто переконайтеся, що ви послідовні (і всі ми використовуємо op =, тому все одно спірне = P)
WhozCraig

7
@PiotrDobrogost: Що поганого у відповіді на запитання? У будь-якому випадку це повинен бути той, хто запитує, який перевірив наявність дублікатів.
Горпік,

4
Мені @PiotrDobrogost здається, ніби ти трохи ... ревнивий ... Якщо ти хочеш об’їхати пошуки дублікатів, спробуй. По-перше, я волію відповідати на запитання, а не шукати дурнів (якщо це питання, яке я спеціально згадую, бачивши раніше). Іноді це може бути швидше, і ergo допоможе тому, хто швидше поставив питання. Також зауважте, що це навіть не цикл. 1є константою, aможе бути нестабільним, визначеним користувачем типом або іншим. Зовсім інші. Насправді, я не розумію, як це взагалі закрилося.
Luchian Grigore

56

Ви можете перевірити, подивившись розбирання, яке буде однаковим.

Для базових типів обидва вони однаково швидкі.

Це виводиться в результаті налагодження (тобто без оптимізації):

    a += x;
010813BC  mov         eax,dword ptr [a]  
010813BF  add         eax,dword ptr [x]  
010813C2  mov         dword ptr [a],eax  
    a = a + x;
010813C5  mov         eax,dword ptr [a]  
010813C8  add         eax,dword ptr [x]  
010813CB  mov         dword ptr [a],eax  

Для певного користувача типів , де ви можете перевантажувати operator +і operator +=це залежить від їх відповідних реалізацій.


1
Не так у всіх випадках. Я виявив, що може бути швидше завантажити адресу пам'яті в регістр, збільшити її та записати назад, ніж збільшити безпосередньо розташування пам'яті (не використовуючи атоми). Я спробую розшукати код ...
Джеймс,

Як щодо користувацьких типів? Хороші компілятори повинні генерувати еквівалентну збірку, але такої гарантії немає.
mfontanini

1
@LuchianGrigore Nope, якщо a1 і xє volatileкомпілятором, який може генерувати inc DWORD PTR [x]. Це повільно.
Джеймс

1
@Chiffa не залежить від компілятора, а залежить від розробника. Ви можете реалізувати, operator +щоб нічого не робити, і operator +=обчислити 100000-те просте число, а потім повернути. Звичайно, це було б глупо робити, але це можливо.
Luchian Grigore

3
@James: якщо ваша програма чутлива до різниці в продуктивності між ++xі temp = x + 1; x = temp;, то, швидше за все, вона повинна бути написана в зборі, а не на c ++ ...
EmirCalabuch

11

Так! Це швидше писати, швидше читати і швидше з’ясовувати, для останнього у випадку, коли це xможе мати побічні ефекти. Тож для людей це в цілому швидше. Людський час взагалі коштує набагато дорожче, ніж комп’ютерний, тому, мабуть, це те, про що ви запитували. Правда?


8

Це насправді залежить від типу x та a та реалізації +. Для

   T x, a;
   ....
   x = x + a;

компілятор повинен створити тимчасовий T, щоб містити значення x +, а він обчислює його, яке потім може призначити x. (Він не може використовувати x або a як робочу область під час цієї операції).

Для x + = a він не потребує тимчасового.

Для тривіальних типів різниці немає.


8

Різниця між x = x + aі x += aполягає в обсязі роботи, яку повинна пройти машина - деякі компілятори можуть (і зазвичай роблять) її оптимізувати, але зазвичай, якщо ми проігноруємо оптимізацію деякий час, трапляється так, що в колишньому фрагменті коду машина повинна xдвічі шукати значення , тоді як в останньому, це пошук має відбуватися лише один раз.

Однак, як я вже згадував, сьогодні більшість компіляторів достатньо розумні, щоб аналізувати інструкції та зменшувати отримані в результаті машинні інструкції.

PS: Перша відповідь на Stack Overflow!


6

Як ви позначили цей C ++, з двох опублікованих вами тверджень неможливо дізнатися. Ви повинні знати, що таке "х" (це трохи схоже на відповідь "42"). Якщо xце POD, то це насправді не буде мати великої різниці. Однак, якщо xце клас, можуть бути перевантаження для operator +і operator +=методів, які можуть мати різну поведінку, що призводить до дуже різного часу виконання.


6

Якщо ви скажете, +=що значно полегшуєте життя компілятору. Щоб компілятор розпізнав те x = x+aсаме x += a, що і компілятор, повинен

  • проаналізуйте ліву сторону ( x), щоб переконатися, що вона не має побічних ефектів і завжди посилається на одне і те ж значення l. Наприклад, це може бути z[i], і він повинен переконатися, що і те, zі інше iне змінюються.

  • проаналізуйте праву сторону ( x+a) і переконайтеся, що це підсумовування, і що ліва частина зустрічається один раз і лише один раз на правій стороні, хоча вона може бути перетворена, як у z[i] = a + *(z+2*0+i).

Якщо то , що ви маєте в виду , щоб додати aдо x, письменник компілятор оцінює це , коли ви просто сказати , що ви маєте в виду. Таким чином, ви не займаєтеся частиною компілятора , що його автор сподівається , що він / вона отримала всі помилки з - за, і це не на самому ділі зробити життя простіше для вас, якщо ви чесно не можете отримати свою голову режиму Fortran.


5

Для конкретного прикладу уявімо простий складний тип числа:

struct complex {
    double x, y;
    complex(double _x, double _y) : x(_x), y(_y) { }
    complex& operator +=(const complex& b) {
        x += b.x;
        y += b.y;
        return *this;
    }
    complex operator +(const complex& b) {
        complex result(x+b.x, y+b.y);
        return result;
    }
    /* trivial assignment operator */
}

У випадку a = a + b він повинен створити додаткову тимчасову змінну, а потім скопіювати її.


це дуже гарний приклад, показує, як реалізовано 2 оператори.
Grijesh Chauhan

5

Ви ставите неправильне запитання.

Це навряд чи вплине на ефективність програми чи функції. Навіть якби це було так, спосіб це дізнатись - скласти профіль коду та знати, як він точно впливає на вас. Замість того, щоб турбуватися на цьому рівні, що швидше, набагато важливіше думати з точки зору чіткості, правильності та читабельності.

Це особливо вірно, якщо врахувати, що навіть якщо це важливий фактор продуктивності, компілятори розвиваються з часом. Хтось може придумати нову оптимізацію, і правильна відповідь сьогодні може завтра стати неправильною. Це класичний випадок передчасної оптимізації.

Це не означає, що продуктивність взагалі не має значення ... Просто те, що це неправильний підхід для досягнення ваших цілей. Правильний підхід - використовувати інструменти профілювання, щоб дізнатись, де ваш код насправді витрачає свій час, а отже, де зосередити свої зусилля.


Звичайно, але це було задумано як запитання низького рівня, а не як велику картину "Коли слід враховувати таку різницю".
Chiffa,

1
Питання ОП було цілком законним, як це показують відповіді інших (і голоси проти). Хоча ми розуміємо вашу думку (спочатку профіль тощо), безумовно , цікаво знати подібні речі - чи справді ви збираєтесь профайлювати кожну тривіальну заяву, яку ви пишете, профілювати результати кожного рішення, яке ви приймаєте? Навіть коли на СО є люди, які вже вивчали, профілювали, розбирали справу та мають загальну відповідь?

4

Я думаю, що це повинно залежати від машини та її архітектури. Якщо його архітектура дозволяє непряму адресацію пам’яті, програма компілятора МОЖЕ просто використати цей код (для оптимізації):

mov $[y],$ACC

iadd $ACC, $[i] ; i += y. WHICH MIGHT ALSO STORE IT INTO "i"

Тоді як i = i + yможе бути перекладений на (без оптимізації):

mov $[i],$ACC

mov $[y],$B 

iadd $ACC,$B

mov $B,[i]


Тим не менш, iслід також подумати про інші ускладнення, наприклад, якщо це функція, що повертає покажчик тощо. Більшість компіляторів виробничого рівня, включаючи GCC, виробляють однаковий код для обох операторів (якщо вони цілі числа).


2

Ні, обидва способи обробляються однаково.


10
Ні, якщо це визначений користувачем тип із перевантаженими операторами.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.