Компиляция GNU/Linux с оптимизацией -O3

Вопрос или проблема

Говорят, что компиляция инструментов GNU и ядра Linux с опцией оптимизации gcc -O3 приводит к странным и забавным ошибкам. Это правда? Кто-нибудь пробовал, или это просто выдумка?

Обновление: В наши дни опасения по поводу -O3, как правило, не обоснованы. -O3 не является опасным или нестабильным и не вносит ошибок в корректный код, однако, как и с любым уровнем оптимизации, неопределенное поведение может проявляться, когда компилятор использует оптимизации -O3. -O3 иногда может создавать более медленный код, например, из-за различных решений о том, когда использовать cmov вместо ветвления и подобных различий, но это также верно практически для любого флага оптимизации. Лучше всего провести тестирование и профилирование. В случае ядра Linux, оказывает ли -O3 какое-либо влияние вообще, сильно зависит от рабочей нагрузки и, вероятно, не должно иметь значения, так как ядро не занимается тяжелыми вычислениями.


-O3 имеет несколько недостатков:

  1. Прежде всего, он часто производит более медленный код, чем -O2 или -Os. Иногда он создает более длинный код из-за разворачивания циклов, что, по сути, может быть медленнее из-за ухудшенного кэширования кода.
  2. Как было сказано, он иногда производит неправильный код. Это может быть либо из-за ошибки в оптимизации, либо из-за ошибки в коде (например, игнорирования строгой алиасности). Так как код ядра иногда бывает «умным», я бы сказал, что возможно, что какой-то разработчик ядра допустил ошибку. Я сталкивался с различными странными проблемами, такими как аварийный останов утилит пользовательского пространства, когда компилировал ядро с gcc 4.5, который на тот момент считался стабильным. Я все еще использую gcc 4.4 для ядра и некоторых выбранных утилит пользовательского пространства из-за различных ошибок. То же самое может применяться и к -O3.
  3. Я не думаю, что он приносит много пользы для ядра Linux. Ядро не занимается тяжелыми вычислениями, и в тех местах, где это происходит, оно оптимизировано на уровне сборки. Флаг -O3 не изменит стоимость переключения контекста или скорость ввода/вывода. Я не думаю, что что-то вроде <0,1% ускорения общей производительности того стоит.

За последние 10 лет я использовал несколько систем Gentoo с более чем 1000 пакетами, используя глобально -O3 -march=native, и еще не сталкивался с какой-либо из мифических проблем стабильности, которые, как предполагается, есть у -O3. Тесты производительности приложений, требующих больших ресурсов процессора (таких как математические/научные приложения), постоянно показывают, что -O3 производит более быстрый код, в конце концов, это было бы бессмысленно, если бы это было не так. Для большинства настольных приложений CFLAGS не имеет большого значения, так как они связаны с вводом/выводом, но это имеет большое значение для серверной части, которая требует больших ресурсов процессора.

Это используется в Gentoo, и я не заметил ничего необычного.

Учтите, что большие части инструментария (в частности, glibc) вообще не компилируются, если вы измените уровни оптимизации. Система сборки настроена на игнорирование ваших предпочтений -O для этих секций в большинстве здравомыслящих дистрибутивов.

Проще говоря, некоторые фундаментальные функции библиотеки и ОС зависят от того, чтобы код действительно делал то, что заявлено, а не то, что было бы быстрее во многих случаях. -fgcse-after-reload в частности (включен в -O3) может вызывать странные проблемы.

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

Да, это старая тема, но на самом деле никто не ответил на вопрос: “Вызывает ли это (опция -O3) действительно ошибки в реальных приложениях? Случалось ли это когда-либо?”

Конечно, я полагаюсь на свой собственный опыт, потому что просто нет другого способа. Я говорю о gcc, начиная с версии 4.4.

Существуют 2 существующих “мифа” о уровне O3 в gcc, но что действительно странно, эти мифы можно найти даже в официальных заявлениях, опубликованных действительно крупными программными компаниями (я не буду перечислять их здесь по понятным причинам).

Миф#1: O3 производит больший код, поэтому он не может поместиться в память кэша, и, следовательно, будет работать медленнее -> на самом деле это O2, которое генерирует больший исполняемый код без значительных преимуществ -> обычно он медленнее, чем O1 – но O3 всегда быстрее, обычно на 20% и более, в основном благодаря векторизации.

Миф#2: O3 нарушает исполняемый код:
Не совсем миф: O3 может повредить код, который не был написан с учетом “O3”. Или, другими словами: O3 повредит программы, которые никогда не предназначались для оптимизации на уровне O3 с помощью gcc. Это обычно требует длительного объяснения, но я постараюсь сделать это как можно короче:

  1. O3 всегда генерирует правильный код, но проблема в том, что оптимизированный код быстрее – это сломает программы с неразрешенными/никогда не протестированными логическими гонками (особенно многопоточные приложения, ядро Linux может оказаться идеальной жертвой в этом случае)

  2. Уровни оптимизации O2/O3 имеют “умную” функцию для устранения “явно” мертвого кода (“явно” никогда не выполняемого ИЛИ кода, который предоставляет результаты, которые “явно” никогда не используются) – это может вызвать поразительные сбои в программах, которые в противном случае работают безупречно на уровне оптимизации O1.

“действительно ли это вызывает ошибки в реальной программе(ах)?”

Да, это бывает:
Мне приходилось добавлять атрибут функции: attribute((optimize(“O1”))) в некоторых случаях, чтобы предотвратить “глупое” удаление вложенных вызовов, используя уровень оптимизации O3 в gcc.

С уважением

Хотя вы можете обойтись использованием -O3 и других настроек оптимизации для большинства приложений (и это может привести к улучшению скорости), я бы не стал использовать такие настройки на самом ядре или на инструментальной цепочке, необходимой для его сборки (компилятор, binutils и т.д.).

Подумайте об этом: стоит ли 5% прироста производительности подсистем raid и ext3 системных сбоев или потенциальной потери и/или повреждения данных?

Настройте все параметры, которые хотите, для Quake порта, в который вы играете, или для аудио/видео кодеков, которые вы используете для записи ваших DVD коллекций в файлы divx. Вы, вероятно, увидите улучшение. Просто не трогайте ядро, если у вас нет времени на пустую трату и данных, которые вы можете позволить себе потерять.

Я предоставил свой опыт здесь для справки.
Я использую GCC7.2.0 в своем проекте, есть сторонний SDK (многопоточность), построенный с использованием -O2, C-программа (многопоточность) построена по умолчанию с -O3 и связана с SDK.

При нагрузочном тестировании я обнаружил, что C-программа вызывает Segmentation Fault в 1 из сотен запусков. Ядро дампа указывало на одну функцию в SDK.
Я несколько раз повторял нагрузочные тесты и всегда сталкивался с Segmentation Fault, и получал одинаковый дамп ядра.

Затем я собрал C-программу с -O2 и связал с SDK. При тысячах запусков не было обнаружено ни одной ошибки.

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

Основываясь на комментариях и ответах на этот вопрос, я думаю, ответ должен быть Да.

Ответ или решение

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

Истина и мифы об оптимизации -O3

  1. Частота появления ошибок и нестабильности: Одним из распространенных заблуждений является мнение о том, что -O3 всегда порождает неустойчивый или неправильный код. На самом деле, подобные ошибки чаще всего возникают не из-за самого компилятора, а из-за нестандартного написания кода, который не предназначен для агрессивной оптимизации. Если код содержит неопределенное поведение, агрессивные оптимизации могут его проявить.

  2. Увеличение размера и производительности: Часто утверждается, что -O3 увеличивает размер кода, что может отрицательно сказываться на производительности из-за ухудшения работы с кешем процессора. С другой стороны, существуют случаи, когда -O3 действительно ускоряет вычислительные задачи, особенно с использованием векторизации.

  3. Потенциальные проблемы с совместимостью: Некоторое программное обеспечение и компоненты, такие как glibc, не могут компилироваться при изменении уровня оптимизации ввиду особенностей их внутренней архитектуры и требований к платформам. Эти участки чаще всего игнорируют -O3 для обеспечения надежности и работоспособности.

Практические рекомендации

  • Тестирование и профилирование: Перед применением -O3 на реальных проектах рекомендуется провести тщательное тестирование и профилирование, чтобы понять влияние оптимизации на производительность и стабильность кода.

  • Использование атрибутов оптимизации: В случаях, когда -O3 не подходит для определённых частей кода, возможно использование атрибутов функции для снижения оптимизационного уровня в выбранных секциях, гарантируя тем самым корректное функционирование программы.

  • Контекст приложения: Для приложений, ориентированных на вычислительные задачи и сервера, -O3 может предложить измеримый прирост производительности. Однако для ядра Linux и других критичных системных компонентов важнее стабильность, а не максимальное быстродействие.

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

Оцените материал
Добавить комментарий

Капча загружается...