Чи повинен я думати про складений машинний код, коли пишу свій код?


20

Наприклад, у мене є такий код:

auto z = [](int x) -> int {
    if (x > 0) {
        switch (x) {
            case 2: return 5;
            case 3: return 6;
            default: return 1;
            }
        }
    return 0;
    };

І пізніше я закликаю це кілька разів. У коді asm я бачу зовнішні дзвінки з лямбда .... щось ... Читати це стає непросто, і я думаю, що це також може спричинити продуктивність. Тож, можливо, я перемагаю в метапрограмуванні, але чи програю я в налагодженні asm та продуктивності? Чи варто уникати сучасних мовних функцій, макросів та інших аспектів метапрограмування, щоб бути впевненим у продуктивності та простоті налагодження?


1
Залежно від версії компілятора та його стандартної бібліотеки, лямбда може бути реалізований неефективно. Дивіться це запитання на Stackoverflow. Однак відповідальність за вдосконалення повинна лежати на постачальнику компілятора.
rwong

15
Вам не потрібно буде налагоджувати код складання, якщо ви не перебуваєте у критичному шляху виконання. Також "чистий код"! = "Хороші показники".
BЈовић

Виправте відступ, будь ласка. Я намагався це зробити, але, здається, ви не можете редагувати просто пробіли.
Крістофер Хаммарстрем

3
@Heather: Здається, ви використовуєте стиль Ратліффа , якого я ніколи не бачив і мені важко читати. Це, безумовно, один з менш відомих. Моє враження було, що ви не відступили належним чином. Не забудьте тоді, якщо ви вважаєте це читабельним, я просто не згоден.
Крістофер Хаммарстрем

1
У ifприкладі коду це зовсім зайве, і хоча компілятор, ймовірно, зрозуміє, що немає підстав спокушатися на поганий прогноз гілки.
dmckee

Відповіді:


59

Чи повинен я думати про складений машинний код, коли пишу свій код?

Ні , не коли ви пишете свій код вперше і не страждаєте від реальних, вимірних проблем з роботою. Для більшості завдань це стандартний випадок. Занадто рано думати про оптимізацію називають «передчасною оптимізацією», і є вагомі причини, чому Д. Кнут назвав це «корінь усього зла» .

Так , коли ви вимірюєте реальне, надійне вузьке місце, і ви визначаєте цю конкретну конструкцію лямбда як першопричину. У цьому випадку може бути гарною ідеєю згадати «закон закопуючих абстракцій» Джоела Спольського і подумати про те, що може статися на рівні зору . Але будьте обережні, ви можете здивуватися, наскільки невеликим буде підвищення продуктивності, коли ви заміните конструкцію лямбда на "не настільки сучасну" мовну конструкцію (принаймні, при використанні гідного компілятора C ++).


2
+1 Короткий, акуратний та простий у догляді за звичайним документом, радий, що ми вас тут.
Джиммі Хоффа

Погоджена, дуже чітка відповідь.
cnd

8

Вибір між лямбда-класом та класом функторів - це компроміс.

Виграш від лямбда здебільшого синтаксичний, мінімізуючи кількість котлоавтомобілів та дозволяючи записати концептуально пов'язаний код всередині функції, яка буде використовувати його (негайно чи пізніше).

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

// define the functor method somewhere
struct some_computer_generated_gibberish_0123456789
{
    int operator() (int x) const
    {
        if (x == 2) return 5;
        if (x == 3) return 6;
        return 0;
    }
};

// make a call
some_computer_generated_gibberish_0123456789 an_instance_of_0123456789;
int outputValue = an_instance_of_0123456789(inputValue);

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

int some_computer_generated_gibberish_0123456789_method_more_gibberish(int x)
{
    if (...) return ...;
    return ...;
}

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

Аспект керування іменами дещо непримітний, і підтримка налагодження лямбда ще знаходиться в зародковому стані . Можна лише сподіватися, що з часом покращиться підтримка налагоджувача.

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


3

Щоб додати відповідь від @DocBrown, пам’ятайте, що в наші дні процесори дешеві, але робоча сила дорога.

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

Тому ваш код повинен оптимізувати технічне обслуговування вище всього іншого, за винятком випадків, коли продуктивність є критичною (і навіть тоді технічне обслуговування потрібно враховувати).


Тільки частково правдиві. Якщо ваш код працює O (n ^ 2) (квадратичний), і ви можете зробити щось краще сказати O (log (n)) (логарифмічний), то апаратне забезпечення ніколи не буде значно збільшувати продуктивність, змінюючи код. У випадку, визначеному оригінальним плакатом, це малоймовірно.
gnash117

@ gnash117 - так, ви маєте рацію, якщо код потрібно запускати багато разів; дякую, що вказали на це. У таких випадках чітке документування коду дозволить підтримувати його, одночасно дозволяючи покращити ефективність.
Paddy Landau

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