Есть ли причина, по которой не стоит использовать оптимизацию времени соединения (LTO)?

GCC, MSVC, LLVM и, возможно, другие наборы инструментов поддерживают оптимизацию во время соединения (целую программу), что позволяет оптимизировать вызовы между модулями компиляции.

Есть ли причина не включать эту опцию при компиляции производственного программного обеспечения?

Ответ 1

Я предполагаю, что под "производственным программным обеспечением" вы подразумеваете программное обеспечение, которое вы отправляете заказчикам/запускаете в производство. Ответы на Почему бы не всегда использовать оптимизацию компилятора? (любезно указано Mankarse) в основном относятся к ситуациям, в которых вы хотите отлаживать свой код (поэтому программное обеспечение все еще находится в разработке фаза - не в производстве).

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

Ответ 2

Этот недавний вопрос вызывает еще один возможный (но скорее специфический) случай, когда LTO может иметь нежелательные последствия: если этот код используется для синхронизации, а отдельная компиляция единицы были использованы, чтобы попытаться сохранить относительный порядок инструментальных и инструментальных утверждений, тогда LTO имеет хорошие шансы уничтожить необходимый порядок.

Я сказал, что это было специфично.

Ответ 3

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

Самый большой недостаток - это резко увеличивает время соединения.

Ответ 4

Помимо этого,

Рассмотрим типичный пример из встроенной системы:

void function1(void) { /*Do something*/} //located at address 0x1000 
void function2(void) { /*Do something*/} //located at address 0x1100
void function3(void) { /*Do something*/} //located at address 0x1200

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

 void (*ptr)(void) = function1;
 (ptr + 0x100)(); //expected to call function2
 (ptr + 0x200)();  //expected to call function3

LOT может привести к неожиданному поведению.

Ответ 5

Один из сценариев, в котором оптимизация времени ссылки может привести к неожиданному поведению неправильного кода, заключается в следующем:

Представьте, что у вас есть два исходных файла read.c и client.c, которые вы компилируете в отдельные объектные файлы. В файле read.c есть функция read, которая не делает ничего, кроме чтения с определенного адреса памяти. Содержимое по этому адресу, однако, должно быть помечено как volatile, но, к сожалению, это было забыто. Из client.c функция read вызывается несколько раз из одной и той же функции. Так как read выполняет только одно чтение адреса, и за пределами функции read оптимизации не происходит, read всегда при вызове обращается к соответствующей ячейке памяти. Следовательно, каждый раз, когда read вызывается из client.c, код в client.c получает только что прочитанное значение из адреса, как если бы использовался volatile.

Теперь, при оптимизации времени соединения, крошечная функция read из read.c, вероятно, будет встроенной там, где она вызывается из client.c. Из-за отсутствия volatile компилятор теперь поймет, что код читает несколько раз с одного и того же адреса, и, следовательно, может оптимизировать доступ к памяти. Следовательно, код начинает вести себя по-другому.