Error ABI
Статья рассматривает проблемы ABI (Application Binary Interface) при обработке ошибок в программировании. Распространённое мнение, что заполнение информации об ошибках "бесплатно" из-за их редкости, неверно. Наивное составление ошибок из алгебраических типов данных (ADT) ухудшает "счастливый путь" выполнения кода. Объекты ошибок, рекурсивно составленные из перечислений, tend to be large, увеличивая size_of<Result<T, E>>, что заставляет функции по всей стеку вызовов использовать возврат больших структур через память. "Вирусность" ошибок означает, что даже одна большая ошибка на редко выполняемом пути ухудшает производительность везде.
Поэтому зрелые библиотеки обработки ошибок скрывают их за тонким указателем, как в Rust (failure и anyhow), но это требует глобального аллокатора, что тоже не бесплатно. Автор предлагает три подхода к возврату результатов: стандартный (как пользовательский тип), более умный (ABI как у T с зарезервированным регистром для E) и радикальный (полное совпадение ABI с -> T и разворот стека для ошибок). Последний, по мнению автора, может быть оптимальным, несмотря на отсутствие надёжных бенчмарков. Вывод: обработка ошибок должна быть специальной для компилятора, особенно в языках со средним уровнем абстракций.
Комментарии (31)
- Адаптивные ABI для статически линкуемых программ могут оптимизировать производительность за счёт контекстного анализа использования функций.
- Проблема "вирусности" больших типов ошибок: даже редкие большие ошибки могут ухудшить производительность всего стека вызовов.
- Альтернативные подходы к обработке ошибок включают тонкие указатели с vtable (anyhow/failure) и разделение Result<T,E> при значительном различии размеров T и E.
- Добавление исключений в Rust вызывает споры: одни видят в этом угрозу производительности, другие — потенциальное решение проблем обработки ошибок.
- Checked exceptions в Java критикуют за необходимость изменения кода при модификации исключений, хотя другие видят в этом преимущество для надёжности кода.
Default musl allocator considered harmful to performance
musl-аллокатор тормозит в 7 раз
Добавь в main.rs:
#[cfg(target_env = "musl")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
и в Cargo.toml:
[target.'cfg(target_env = "musl")'.dependencies]
mimalloc = "0.1"
Проблема — конкуренция потоков за malloc. Чем больше потоков, тем хуже.
Замена аллокатора нужна даже для однопоточных программ: забудешь — потом дорого.
Почему musl? Статика + 2 МБ distroless-контейнер = старые RH и быстрый cold-start.
Кейс: сервер обрабатывал данные в 7 раз медленнее хоста.
Виноват 200 000 контекст-переключений/сек vs 1 200 у glibc.
strace показал 6,7 с в futex у musl против 0,5 с у glibc.
Комментарии (53)
- musl-аллокатор медленен в многопоточных Rust-программах из-за одного глобального замка.
- Альпийские образы компактны, но «размер» ≠ «скорость»; в проде чаще берут Debian/RHEL.
- Замена: jemalloc, mimalloc или Wolfi (glibc, apk, busybox) без смены менеджера пакетов.
- glibc тоже фрагментирует память; MALLOC_ARENA_MAX=jemalloc спасает.
- Новый mallocng musl не решает конкуренцию потоков: цель musl — минимум кода и харднинг, а не perf.