Когда происходит оптимизация кода? - PullRequest
5 голосов
/ 04 февраля 2012

Вчера у меня было интервью. Там меня спросили, когда происходит оптимизация кода? Скажем,

int abc;//Global variable
abc = 3;
if(abc == 3)
{
  printf("abc will be always 3");
}
else
{
  printf("This will never executed");
}

Теперь вопрос, когда происходит оптимизация? A ... Во время выполнения B ... Во время компиляции. Я ответил во время компиляции ... Мне казалось, компилятор проверяет ключевое слово volatile во время компиляции. Если переменная не объявлена ​​как volatile, она оптимизирует код. Но когда компилятор узнает, что эта переменная никогда не будет отлична от 3? Если это во время выполнения, то когда компилятор узнает, что переменная никогда не будет отлична от 3? Потому что, если переменная будет изменена после выполнения этой части кода. Пожалуйста, очистите мои сомнения

Ответы [ 7 ]

8 голосов
/ 04 февраля 2012

C-код обычно компилируется с использованием статической (или опережающей) компиляции.Код устанавливается в камне, как только он покидает компилятор;это не может измениться во время выполнения.

Это в отличие от языков, которые используют компиляция точно в срок , таких как Java.Там, в то время как программа работает, оптимизация может происходить в значительной степени в любое время.

4 голосов
/ 04 февраля 2012

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

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

Код, подобный этому, как написано, позволяет оптимизировать время компиляции, поскольку компилятор может проверять любойвозможные альтернативные значения abc и, если ничего не найдено, оптимизировать вызов.Однако объем поиска сильно влияет на то, может ли это произойти, и настройки компилятора влияют на это.

Если компилятор просто наивно оптимизирует отдельные объектные файлы, а ваша вторая строка находится в другом файле из раздела печати,тогда он не сможет гарантировать, что abc не изменится, и поэтому не сможет оптимизировать это вообще.Независимо от использования переменных, это зависит от того, насколько агрессивны ваши настройки компилятора и разрешено ли им сбрасывать мертвые ветви, или рассмотрит это.Оптимизация по размеру может с большей вероятностью удалить ветку, чем по скорости, но средние / высокие настройки, скорее всего, сделают это в любом случае (если возможно).

У большинства современных компиляторов есть опция оптимизации всей программы,задерживает большую часть оптимизации до стадии компоновщика.Это позволяет ему выполнять поиск по всей программе, потенциально обнаруживать, что abc никогда не изменяется и не используется где-либо еще, и удалять переменную и ошибочную ветвь из условия.Оптимизация всей программы может быть гораздо более эффективной, чем отдельная оптимизация для каждого объекта, поскольку она позволяет более точный поиск.

В случае, когда компилятор не может обрезать мертвый код, вы можете либо намекнуть на это (последние конструкции, такие как constexpr, могут помочь в этом) или добавить оптимизации самостоятельно.Это может быть так же просто, как сначала указать наиболее вероятный путь и включить возврат перед другим, что избавит процессор от скачка.Такая микрооптимизация вряд ли понадобится, конечно, не в таком простом примере, как этот, где if само по себе является достаточной оптимизацией.

3 голосов
/ 09 февраля 2012

Не отвечая на вопрос, но приводя пример оптимизации времени компиляции. gcc оптимизирует код, когда его об этом попросят. Опция -O (Оптимизировать) включает оптимизацию на разных уровнях. Может использоваться как -O1, -O2 и -O3. Страница man gcc точно описывает значение каждого уровня.

Опция -S переводит C в сборку и сохраняет в файл .s.

test.c

#include <stdio.h>

int abc;//Global variable

void main()
{
    abc = 3;
    if(abc == 3)
        printf("abc will be always 3");
    else
        printf("This will never executed");
}

Без оптимизации gcc две строки появляются в коде сборки.

$ gcc -S test.c; cat test.s

    .file   "test.c"
    .comm   abc,4,4
    .section    .rodata
.LC0:
    .string "abc will be always 3"
.LC1:
    .string "This will never executed"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $3, abc(%rip)
    movl    abc(%rip), %eax
    cmpl    $3, %eax
    jne .L2
    movl    $.LC0, %eax
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf
    jmp .L1
.L2:
    movl    $.LC1, %eax
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf
.L1:
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)"
    .section    .note.GNU-stack,"",@progbits

При оптимизации gcc уровня 1 только одна строка переводится в сборку

$ gcc -O1 -S test.c; кошачий тест.s

    .file   "test.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "abc will be always 3"
    .text
    .globl  main
    .type   main, @function
main:
.LFB11:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, abc(%rip)
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE11:
    .size   main, .-main
    .comm   abc,4,4
    .ident  "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)"
    .section    .note.GNU-stack,"",@progbits
2 голосов
/ 04 февраля 2012

Во время компиляции:)

(ну, в принципе, это зависит от компилятора и т. Д.)

1 голос
/ 04 февраля 2012

Самый простой ответ: Оптимизация кода происходит во время написания.

Простой ответ: возможно, это зависит от компилятора с учетом области

Сложный ответ: Учитывая глобальную область видимости, компилятор должен оставить это в покое, предполагая, что к нему можно получить доступ в другом месте файла. Многократные проходы могут оптимизировать это. Если компилятор не считает его статичным по отношению к файлу (например, системы с плоской моделью памяти), то глобальный действительно глобален, и предположение должно быть любым, что может изменить значение. Вот почему вы избегаете глобальных переменных, если целью не является глобальный доступ.

В зависимости от процессора может применяться аргумент прогнозирования ветвления, но в основном это время компиляции или нет вообще.

ps: мне очень не нравятся подобные вопросы на собеседовании.

1 голос
/ 04 февраля 2012

Интересно, они искали один или несколько возможных ответов.

Обычно компилятор ничего не делает во время выполнения.Двоичный файл компилятора и его компоненты не обязательно должны присутствовать (для языка C) в среде выполнения.

Тип оптимизации, который устранял бы эту мертвую ветвь, использовал бы определенные методы для написания оптимизирующих компиляторов.Смотрите книгу Мучника о компиляторах.Используемые методы могут включать создание ориентированного графа базовых блоков с последующим использованием одного из:

  • анализа использования по умолчанию
  • статического анализа одного назначения (ssa)вместе с
  • постоянным распространением (преобразовать if в if (3 == 3)а затем
  • исключение константного выражениязатем
  • удаление мертвого кода / мертвой ветви

С другой стороны, некоторый компилятор может не рассчитать достаточно, чтобы удалить это во время оптимизации.

В этом случае, еслиВы запустили код на чипе с предсказанием ветвления, чип «узнает», что предсказана первая ветвь, и выгодно кеширует (выбирает) эту ветвь.Но это не механизм компиляции, обычно не называемый оптимизацией.

1 голос
/ 04 февраля 2012

Я не слишком знаком с тем, как компилятор оптимизирует код, однако я знаю, что из имеющегося там кода компилятор может сделать вывод, что abc никогда не изменяется.Это означает, что может полностью исключить оператор if и просто вызвать первую printf функцию.

Затем во время выполнения не остается большой оптимизации, так как коддовольно просто.Если abc изменить, то, очевидно, if не может быть исключено, однако во время выполнения существуют такие вещи, как прогнозирование ветвления на ЦП, которое будет пытаться предсказать путь кода, который будет выбран.Это также можно рассматривать как форму оптимизации.

Примечание: я не утверждаю, что это именно то, что происходит, но я мог видеть, что компилятор может оптимизировать таким образом в агрессивной настройке оптимизации.

...