Cargo

Link: Cargo profiles - https://doc.rust-lang.org/cargo/reference/profiles.html Mold linker - https://github.com/rui314/mold Условная компиляция - https://doc.rust-lang.org/reference/conditional-compilation.html

Cargo

Use Cargo for managing projects.

cargo new test_project // create project with binary file src/main.rs
// OR
cargo new test_project --lib // create project with library file src/lib.rs 

cd test_project

Source code is in src folder.

cargo build # build project for debug, do not run
cargo run # build & run project
cargo check # fast compiling
cargo build --release # slow build with optimizations for speed of release version

Documentation of methods & traits used in code can be compiled an opened in browser with Cargo command:

cargo doc --open

Зависимости кода в cargo.toml

  • [dependencies] — Package library dependencies - видно отовсюду, из тестов;
  • [dev-dependencies] — Dependencies for examples, tests, and benchmarks. При сборке ПО видны только для тестов (в рамках #[cfg(test)] mod tests {});
  • [build-dependencies] — Dependencies for build scripts.

Сборка бинарных файлов

Можно в src папке создать подпапку bin, и там сложить вариации бинарных файлов. После чего указывать при сборке cargo run --bin <название файла в папке bin>

Сборка Examples

Create “examples” folder beside the “src” folder. Create a file named, for example, “variables.rs” and place some test code in the folder. Then in the project root folder run:

cargo run --example variables

❗Примеры в Examples папке наследуют features и прочие параметры из общего Cargo.toml. В случае условной компиляции (см, далее) команда будет выглядеть: cargo run --example variables --features mock

Условная компиляция

Позволяет исключить часть кода в зависимости от условий.

Атрибут #[cfg(condition)]

#[cfg(target_os = "windows")]
fn win_foo() {}

Атрибут #[cfg_attr(condition, attribute)]

#[cfg_attr(target_os = "linux", path = "linux.rs")]
#[cfg_attr(windows, path = "windows.rs")]
mod os;

Макрос cfg!(condition)

let os = if cfg!(unix) {"unix"} else {"windows"};

Можно комбинировать условия с помощью all(), any(), not():

#[cfg(all(unix, target_pointer_width = "32"))]
fn on_32bit_unix() {}

Mock-компиляция

Вариант условной компиляции в целях тестирования:

#[cfg(feature = "debug")] // код сработает только с флагом debug
fn get_data() -> String {
    "мулька".to_string()
}

#[cfg(feature = "mock", feature = "debug")] // код сработает только с флагами mock И debug
fn get_data() -> String {
    "хитрая мулька".to_string()
}

#[cfg(not(feature = "debug"))] // код сработает без флага debug
fn get_data() -> String {
    "реальные данные".to_string()
}

fn main() {
    println!("{}", get_data());
}

Features нужно специально включать при сборке командой cargo run --features debug. Либо можно прописать флаг включения default = ["debug"] в файле Cargo.toml:

[features]
default = ["debug"] # по-умолчанию флаг = ВКЛ
debug = []

Отключить работу флага default можно командой cargo run --no-default-features.

Условная компиляция, встроенная в Cargo, позволяет подключать зависимости опционально. Можно проверять фичи в коде:

#[cfg(feature = "win")]
f use_winapi() {
let void = winapi::ctypes::c_int::from(t : 5);
let f_typ = winapi_util::console::Color::Blue;
}

Пример с подключением библиотеки rand только для создания случайного значения в “mock-варианте” кода:

[features]
default = ["mock"] # включить флаг mock по-умолчанию
mock = ["rand"] # использовать рандимозатор только при включенном флаге
mock-persistent = ["rand"]

[dependencies]
rand = { version = "0.10.0", optional = true } # опциональный рандомизатор

Диагностика сборки

cargo-bloat для поиска проблем

cargo install cargo-bloat
cargo bloat --release

Определяет, какие зависимости задерживают сборку.

cargo build –timings

Создаёт отчёт в HTML с указанием трат времени:

cargo build --timings
# далее открыть target/cargo-timings/cargo-timing.html

cargo-llvm-lines

Показывает, какие generic-функции создают больше всего нагрузки для LLVM:

cargo install cargo-llvm-lines
cargo llvm-lines --release

Generics создают мономорфизм - каждый новый тип создаёт новый код при сборке.

Ускорение сборки

Профили Cargo

Можно управлять ходом сборки проекта с помощью профилей. В том числе это сильно виляет на скорость сборки. Примеры профилей, которые можно создать в cargo.toml файле:

[profile.dev]
opt-level = 0          # No optimization (fastest compilation LLVM)
debug = 1              # Line info only (not full debug symbols)
codegen-units = 256    # More parallelism (max is 256)
incremental = true     # Recompile only changed code

[profile.release]
opt-level = 3          # Maximum runtime performance
lto = "thin"           # Faster than "fat" LTO, still good optimization
codegen-units = 1      # Better optimization at cost of compile time

Для debug сборок, opt-level = 0 и высокое число codegen-units даю самую высокую скорость сборки (разница в скорости в 108 раз!).

Кеширование сборки зависимостей

С помощью sccache можно кешировать собранные артефакты, в том числе таскать кеш между системами.

# Установка
cargo install sccache

# Применение
export RUSTC_WRAPPER=sccache
cargo build

cargo-wizard - быстрая оптимизация

Простой инструмент выбора типа оптимизации:

cargo install cargo-wizard
cargo wizard

Даёт 3 шаблона: fast compilationfast execution, и small binary size

Удалить лишние неиспользуемые зависимости

cargo install cargo-machete
cargo machete  # находит неиспользуемые crates

Создание RAM-disk для пути сборки

Linux/macOS:

mkdir -p /tmp/rust-target
mount -t tmpfs -o size=8G tmpfs /tmp/rust-target
export CARGO_TARGET_DIR=/tmp/rust-target

Управление потоками сборки

Использовать все ядра, кроме 2 (чтобы ОС не повисла):

cargo build -j $(nproc --ignore=2)

Либо указать в .cargo/config.toml:

[build]
jobs = 8

Panic Response

В ответ на панику, по умолчанию программа разматывает стек (unwinding) - проходит по стеку и вычищает данные всех функций. Для уменьшения размера можно просто отключать программу без очистки - abort. Для этого в файле Cargo.toml надо добавить в разделы [profile]:

[profile.release]
panic = 'abort'

Mold (только для Linux)

Проект для Linux (в macOS есть коммерческий аналог Sold, который ненамного лучше сборщику в XCode 15+, потому использование не оправдано).

Настройка

  • Ubuntusudo apt-get install mold clang
  • Fedorasudo dnf install mold clang
  • Arch Linuxsudo pacman -S mold clang

Создать в проекте папку и файл .cargo/config.toml (либо для всех проектов сразу - папка и файл ~/.cargo/config.toml) и вписать:

[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]

Где архитектуру нужно заменить на текущую: узнать её командой rustc -vV.

Проверка работы

Для проверки в папке с бинарным файлом запустить команду:

$ readelf -p .comment target/debug/your-app

String dump of section '.comment':
  [     0]  mold 2.4.0 ( ... )