вызов вложенной функции быстрее или нет? - PullRequest
3 голосов
/ 25 апреля 2010

У меня глупый спор с другом, и мне нужно авторитетное слово.

У меня есть эти два фрагмента, и я хочу знать, какой из них быстрее? [A или B]

(при условии, что компилятор ничего не оптимизирует)

[A]

if ( foo () ); 

[B] * * 1 010

int t = foo ();
if ( t )

EDIT : Ребята, это может показаться вам глупым вопросом, но у меня есть друг по аппаратному обеспечению , который утверждал, что даже БЕЗ оптимизации (возьмите любой процессор, любую пару компиляторов) ) CASE B всегда быстрее, потому что он НЕ извлекает память для результата предыдущей инструкции, но напрямую обращается к результату из Common Data Bus, минуя эти данные (вспомним 5-ступенчатый конвейер).

Хотя Мой аргумент заключался в том, что без того, чтобы компилятор не сообщал, сколько данных копировать или проверять, это невозможно сделать (вам нужно пойти в память, чтобы получить данные, БЕЗ компилятора, оптимизирующего это)

Ответы [ 6 ]

13 голосов
/ 25 апреля 2010

«Оптимизация», необходимая для преобразования [B] в [A], настолько тривиальна (особенно, если t нигде не используется), что компилятор, вероятно, даже не вызовет это оптимизация , Это может быть чем-то, что само собой разумеется, независимо от того, включена оптимизация или нет.

Единственный способ узнать это - попросить компилятор сгенерировать листинг исходного кода для обоих битов кода, а затем сравнить их.

4 голосов
/ 25 апреля 2010

Резюме
1. Мы говорим о наносекундах. За это время свет перемещается на 30 см. 2. Иногда, если вам действительно повезло, [A] быстрее


Примечание: [B] может иметь другое значение
если тип возвращаемого значения foo не int, а объект, который имеет неявные преобразования как int, так и bool, выполняются различные пути кода. Можно содержать Sleep.

Предполагая, что функция возвращает int:

Зависит от компилятора
Даже с ограничением «без оптимизации» нет никакой гарантии, как будет выглядеть сгенерированный код. B может быть в 10 раз быстрее, а компилятор все равно будет совместимым (и вы, скорее всего, этого не заметите).

Зависит от аппаратного обеспечения
В зависимости от вашей архитектуры, сгенерированный код может даже не отличаться, независимо от того, сколько пытается ваш компилятор.

Предполагается, что современный компилятор на современной архитектуре x86 / x64:

На типичных компиляторах разница не более чем незначительна
который хранит t в переменной стека, две дополнительные загрузки стека обычно стоят 2 такта (меньше, чем наносекунда на моем процессоре). Это ничтожно мало по сравнению с «сопутствующей стоимостью» - звонком на foo, стоимостью самой foo и филиалом. Неоптимизированный вызов с полным стеком может легко стоить вам 20.200 циклов в зависимости от формы.

Для сравнения: стоимость цикла одного обращения к памяти, не входящей в кэш 1-го уровня (примерно: 100 циклов со 2-го уровня, 1000 с основного, сотни тысяч с диска)

... или даже не существует
Даже если ваш компилятор не оптимизирует, ваш процессор может. Из-за создания пары / микрокода стоимость цикла может фактически быть идентичной.

3 голосов
/ 25 апреля 2010

Для записи, gcc, при компиляции с специально отключенной оптимизацией (-O0), выдает различный код для двух входов (в моем случае тело foo было return rand();, так что результат не был бы определяется во время компиляции).

Без временной переменной t:

        movl    $0, %eax
        call    foo
        testl   %eax, %eax
        je      .L4
        /* inside of if block */
.L4:
        /* rest of main() */

Здесь возвращаемое значение foo сохраняется в регистре EAX, и регистр проверяется на самом себе, чтобы определить, равен ли он 0, и если да, то он перепрыгивает через тело блока if.

С временной переменной t:

        movl    $0, %eax
        call    foo
        movl    %eax, -4(%rbp)
        cmpl    $0, -4(%rbp)
        je      .L4
        /* inside of if block */
.L4:
        /* rest of main() */

Здесь возвращаемое значение foo сохраняется в регистре EAX, а затем помещается в стек. Затем содержимое местоположения в стеке сравнивается с литералом 0, и если они равны, оно перепрыгивает через тело блока if.

И поэтому, если мы далее предположим, что процессор не выполняет каких-либо «оптимизаций», когда он генерирует для этого микрокод, то версия без временной должна быть на несколько тактов быстрее. Это не будет существенно быстрее, потому что, хотя версия с временным использованием проталкивания стека, значение стека почти наверняка будет в кеше L1 процессора, когда инструкция сравнения выполняется сразу после слов, и поэтому не будет быть в оба конца в оперативной памяти.

Конечно, код становится идентичным, как только вы включаете любой уровень оптимизации, даже -O1, и кто скомпилирует все, что настолько критично, что им небезразлично несколько тактов при всех отключенных оптимизациях?

Редактировать: Что касается вашей дополнительной информации о вашем друге аппаратного инженера, я не могу понять, как доступ к значению в кэше L1 будет когда-либо на быстрее , чем доступ к реестру непосредственно. Я мог бы видеть, что он будет примерно таким же быстрым , если значение никогда не покидает конвейер, но я не могу видеть, что он быстрее , тем более что он все еще должен выполнять movl Инструкция в дополнение к сравнению. Но покажите ему код сборки выше и спросите, что он думает; это будет более продуктивно, чем пытаться обсудить проблему с точки зрения C.

2 голосов
/ 25 апреля 2010

Вероятно, они оба будут одинаковыми. Это int будет храниться в регистре в любом случае.

0 голосов
/ 25 апреля 2010

Это действительно зависит от того, как построен компилятор. Но я думаю, что в большинстве случаев А будет быстрее. И вот почему:

В B компилятору может не понадобиться выяснять, используется ли t когда-либо снова, поэтому он будет вынужден сохранить значение после оператора if. А это может означать, что он будет помещен в стек.

0 голосов
/ 25 апреля 2010

A, вероятно, будет чуть-чуть быстрее, потому что он не выполняет присваивание переменных. Разница, о которой мы говорим, слишком мала, чтобы ее можно было измерить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...