Оптимизации: компромисс между скоростью и размером
Каждый хочет, чтобы его программа была очень быстрой и очень компактной, но обычно невозможно достичь обеих характеристик одновременно. В этом разделе обсуждаются различные уровни оптимизации, предоставляемые компилятором rustc, и их влияние на время выполнения и размер бинарного файла программы.
Без оптимизаций
Это настройка по умолчанию. Когда вы вызываете cargo build, используется профиль разработки (также известный как dev). Этот профиль оптимизирован для отладки, поэтому он включает отладочную информацию и не включает никаких оптимизаций, т.е. используется -C opt-level = 0.
По крайней мере, для разработки без операционной системы отладочная информация не занимает места во флэш-памяти / ПЗУ, поэтому мы рекомендуем включать отладочную информацию в профиле выпуска — по умолчанию она отключена. Это позволит использовать точки останова при отладке сборок выпуска.
[profile.release]
# символы полезны, и они не увеличивают размер во флэш-памяти
debug = true
Отсутствие оптимизаций отлично подходит для отладки, поскольку пошаговое выполнение кода ощущается как выполнение программы оператор за оператором, плюс вы можете выводить значения локальных переменных и аргументов функций в GDB. При оптимизированном коде попытка вывести переменные приводит к сообщению $0 = <value optimized out>.
Самый большой недостаток профиля dev заключается в том, что получаемый бинарный файл будет огромным и медленным. Размер обычно представляет большую проблему, поскольку неоптимизированные бинарные файлы могут занимать десятки килобайт флэш-памяти, которой может не быть на вашем целевом устройстве — в результате неоптимизированный бинарный файл просто не помещается в ваше устройство!
Можно ли получить меньшие бинарные файлы, удобные для отладки? Да, есть один прием.
Оптимизация зависимостей
Есть функция Cargo под названием profile-overrides, которая позволяет переопределять уровень оптимизации для зависимостей. Вы можете использовать эту функцию, чтобы оптимизировать все зависимости для размера, сохраняя верхний крейт неоптимизированным и удобным для отладки.
Учтите, что обобщенный код может быть проблематичным при использовании различных уровней оптимизации, поэтому вам может потребоваться экспериментировать с настройками.
Оптимизация для скорости
Если вы хотите, чтобы ваши бинарные файлы выпуска были оптимизированы для скорости, измените настройку profile.release.opt-level в Cargo.toml, как показано ниже:
[profile.release]
opt-level = 3
или
[profile.release]
opt-level = 2
Эти два уровня оптимизации (opt-level = 2 и 3) значительно увеличивают производительность, но также могут увеличивать размер бинарного файла. Если вы не можете позволить себе увеличение размера, вам следует оптимизировать программу для размера.
Оптимизация для размера
По состоянию на 18.09.2018 rustc поддерживает два уровня оптимизации для размера: opt-level = "s" и "z". Эти названия унаследованы от clang / LLVM и не слишком описательны, но "z" подразумевает, что он производит бинарные файлы меньшего размера, чем "s".
Если вы хотите, чтобы ваши бинарные файлы выпуска были оптимизированы для размера, измените настройку profile.release.opt-level в Cargo.toml, как показано ниже:
[profile.release]
# или "z"
opt-level = "s"
Эти два уровня оптимизации значительно снижают порог встраивания LLVM, метрику, используемую для принятия решения о встраивании функции. Одним из принципов Rust являются абстракции с нулевой стоимостью; эти абстракции часто используют множество новых типов и небольших функций для сохранения инвариантов (например, функции, которые заимствуют внутреннее значение, такие как deref, as_ref), поэтому низкий порог встраивания может привести к тому, что LLVM упустит возможности оптимизации (например, устранение мертвых ветвей, встраивание вызовов замыканий).
При оптимизации для размера вы можете попробовать увеличить порог встраивания, чтобы проверить, влияет ли это на размер бинарного файла. Рекомендуемый способ изменения порога встраивания — добавить флаг -C inline-threshold к другим флагам rustflags в .cargo/config.toml:
# .cargo/config.toml
# предполагается, что используется шаблон cortex-m-quickstart
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
# ..
"-C", "inline-threshold=123", # +
]
Какое значение использовать? По состоянию на версию 1.29.0 следующие пороги встраивания используются для разных уровней оптимизации:
opt-level = 3использует 275opt-level = 2использует 225opt-level = "s"использует 75opt-level = "z"использует 25
При оптимизации для размера стоит попробовать значения 225 и 275.