Skip to content

RFC-031:Уровни оптимизации и менеджер проходов

Справка:

Аннотация

В данном документе предлагается введение в YaoXiang системы уровней оптимизации и менеджера проходов, которые изменят компиляционную оптимизацию с модели «всё или ничего» на настраиваемые пакеты оптимизации. Уровни оптимизации (O0-O3) определяют различные комбинации стратегий оптимизации, а менеджер проходов отвечает за выполнение проходов оптимизации в порядке с учётом зависимостей. Документ также определяет стандартный интерфейс для проходов оптимизации, закладывая архитектурную основу для будущих расширений (мономорфизация, встраивание, свёртка констант и др.).

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

Мотивация

Зачем нужны уровни оптимизации?

Текущий компилятор не имеет конфигурации оптимизации — весь код проходит через одинаковый процесс обработки. Это приводит к следующим проблемам:

  1. Плохой опыт отладки: при отладке оптимизация не нужна, но её нельзя отключить
  2. Невозможность контролировать размер бинарного файла: мономорфизация обобщённых типов раздувает бинарник, но её нельзя отключить
  3. Неконтролируемая скорость компиляции: нельзя выбрать между «быстрой компиляцией» и «глубокой оптимизацией» в зависимости от сценария
  4. Беспорядочное выполнение проходов оптимизации: в будущем несколько проходов оптимизации будут иметь зависимости между собой, что требует единого управления

Текущие проблемы

yaoxiang
# Текущее состояние: весь код проходит одинаковую обработку
# - При отладке: оптимизация не нужна, но отключить нельзя
# - В продакшене: нужна оптимизация, но нельзя настроить глубину
# - Для обобщённых функций: генерируется несколько копий кода, но нельзя управлять

identity: (T: Type) -> (x: T) -> T = (x) => x
x = identity(42)        # сгенерирует identity_Int
s = identity("hello")   # сгенерирует identity_String
# Пользователь не может выбрать «не мономорфизировать» (режим стирания типов)

Ценность уровней оптимизации

СценарийТребованиеУровень оптимизации
Разработка и отладкаБыстрая компиляция, сохранение отладочной информацииO0
Повседневная разработкаБазовая оптимизация, баланс скорости компиляцииO1
Тестирование/CIСтандартная оптимизация, проверка продакшен-поведенияO2
Продакшен-релизГлубокая оптимизация, максимальная производительностьO3
Скрипты/быстрое прототипированиеАвтоматический выбор (в зависимости от целевой платформы)Auto

Предложение

Основной дизайн

1. Определение уровней оптимизации

rust
/// Уровень оптимизации
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum OptLevel {
    /// O0: без оптимизации (режим отладки)
    /// - Сохраняется вся отладочная информация
    /// - Никаких оптимизационных преобразований
    /// - Максимальная скорость компиляции
    /// - Применение: разработка, быстрая итерация
    O0,

    /// O1: базовая оптимизация (по умолчанию)
    /// - Мономорфизация по требованию (не генерируются неиспользуемые специализации)
    /// - Базовая свёртка констант
    /// - Базовое удаление мёртвого кода
    /// - Применение: повседневная разработка
    #[default]
    O1,

    /// O2: стандартная оптимизация
    /// - Мономорфизация по требованию
    /// - Полная свёртка констант
    /// - Полное удаление мёртвого кода
    /// - Встраивание маленьких функций
    /// - Оптимизация хвостовых вызовов
    /// - Применение: тестирование, CI, продакшен
    O2,

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

    /// Auto: автоматический выбор
    /// - Автоматический выбор стратегии оптимизации в зависимости от целевой платформы и доступных ресурсов
    /// - Применение: скрипты, быстрое прототипирование
    Auto,
}

2. Интерфейс прохода оптимизации

rust
/// Интерфейс прохода оптимизации
pub trait OptimizationPass {
    /// Имя прохода (для логов и объявления зависимостей)
    fn name(&self) -> &str;

    /// Выполнить проход
    fn run(&self, module: &mut ModuleIR, config: &PassConfig) -> PassResult;

    /// От каких проходов зависит выполнение данного
    fn dependencies(&self) -> Vec<&str> {
        vec![]
    }

    /// Должен ли проход выполняться при текущей конфигурации
    fn should_run(&self, config: &PassConfig) -> bool {
        true
    }
}

/// Конфигурация прохода
#[derive(Debug, Clone)]
pub struct PassConfig {
    /// Уровень оптимизации
    pub opt_level: OptLevel,
    /// Включена ли отладочная информация
    pub debug_info: bool,
    /// Целевая платформа
    pub target_platform: TargetPlatform,
}

/// Результат выполнения прохода
#[derive(Debug, Default)]
pub struct PassResult {
    /// Изменён ли IR
    pub changed: bool,
    /// Статистика
    pub stats: PassStats,
}

/// Статистика прохода
#[derive(Debug, Default)]
pub struct PassStats {
    /// Количество встроенных функций
    pub functions_inlined: usize,
    /// Количество мономорфизированных функций
    pub functions_monomorphized: usize,
    /// Количество удалённого мёртвого кода
    pub dead_code_removed: usize,
    /// Количество свёрнутых констант
    pub constants_folded: usize,
}

3. Менеджер проходов

rust
/// Оптимизатор
pub struct Optimizer {
    /// Список зарегистрированных проходов (отсортирован по зависимостям)
    passes: Vec<Box<dyn OptimizationPass>>,
}

impl Optimizer {
    /// Создать оптимизатор для заданного уровня оптимизации
    pub fn for_opt_level(level: OptLevel) -> Self {
        let passes = Self::create_passes_for_level(level);
        Self { passes }
    }

    /// Создать список проходов для заданного уровня
    fn create_passes_for_level(level: OptLevel) -> Vec<Box<dyn OptimizationPass>> {
        match level {
            OptLevel::O0 => {
                vec![
                    // Режим отладки: минимум оптимизации, только необходимая очистка
                    Box::new(ConstFoldPass::minimal()),
                ]
            }
            OptLevel::O1 => {
                vec![
                    // Базовая оптимизация
                    Box::new(ConstFoldPass::basic()),
                    Box::new(MonomorphizePass::on_demand()),
                    Box::new(DcePass::basic()),
                ]
            }
            OptLevel::O2 => {
                vec![
                    // Стандартная оптимизация
                    Box::new(ConstFoldPass::full()),
                    Box::new(MonomorphizePass::on_demand()),
                    Box::new(DcePass::full()),
                    Box::new(InlinePass::small_functions()),
                    Box::new(TcoPass::new()),
                ]
            }
            OptLevel::O3 => {
                vec![
                    // Агрессивная оптимизация
                    Box::new(ConstFoldPass::full()),
                    Box::new(MonomorphizePass::full()),
                    Box::new(InlinePass::aggressive()),
                    Box::new(DcePass::full()),
                    Box::new(TcoPass::new()),
                    // Дополнительные агрессивные оптимизации...
                ]
            }
            OptLevel::Auto => {
                // Автоматический выбор: определяется целевой платформой
                Self::create_passes_for_level(OptLevel::O1)
            }
        }
    }

    /// Выполнить все проходы оптимизации
    pub fn run(&self, module: &mut ModuleIR, config: &PassConfig) -> OptimizerResult {
        let mut total_stats = OptimizerStats::default();

        for pass in &self.passes {
            if !pass.should_run(config) {
                continue;
            }

            let result = pass.run(module, config);
            total_stats.merge(result.stats);
        }

        OptimizerResult {
            module: module.clone(),
            stats: total_stats,
        }
    }
}

Примеры

Использование в командной строке

bash
# Режим отладки: без оптимизации
yaoxiang build --opt-level O0

# Повседневная разработка: базовая оптимизация (по умолчанию)
yaoxiang build

# Продакшен-релиз: стандартная оптимизация
yaoxiang build --opt-level O2

# Максимальная производительность: агрессивная оптимизация
yaoxiang build --opt-level O3

# Автоматический выбор
yaoxiang build --opt-level Auto

Конфигурационный файл

json
{
  "optimization_level": "O2",
  "mono": {
    "enabled": true,
    "strategy": "OnDemand"
  },
  "debug_info": false
}

Использование API

rust
use yaoxiang::frontend::{Compiler, CompileConfig, OptLevel};

// Режим отладки
let config = CompileConfig::new()
    .with_opt_level(OptLevel::O0);
let mut compiler = Compiler::with_config(config);

// Продакшен-режим
let config = CompileConfig::new()
    .with_opt_level(OptLevel::O2);
let mut compiler = Compiler::with_config(config);

Синтаксические изменения

Синтаксических изменений нет. Уровень оптимизации — это конфигурация компилятора, не влияющая на синтаксис языка.

Детальный дизайн

Соответствие уровней оптимизации и проходов

ПроходO0O1O2O3Описание
Свёртка константМин.Баз.ПолнаяПолнаяВычисление константных выражений на этапе компиляции
МономорфизацияПо требов.По требов.ПолнаяСпециализация обобщённых функций
Удаление мёртвого кодаБаз.ПолноеПолноеУдаление недостижиемого или неиспользуемого кода
Встраивание функцийМелкие ф-цииАгрессивноеВставка тела функции в место вызова
Оптимизация хвостовых вызововПреобразование хвостовой рекурсии в цикл
Анализ побегаОпределение распределения в стеке/куче
Оптимизация цикловРазвёртка циклов, вынос инвариантов

Стратегии мономорфизации

rust
/// Стратегия мономорфизации
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum MonoStrategy {
    /// Не мономорфизировать — стирание типов, обобщённые функции имеют только одну копию кода
    /// Преимущества: маленький бинарник, быстрая компиляция
    /// Недостатки: накладные расходы динамической диспетчеризации в runtime
    Erased,

    /// Мономорфизация по требованию — генерация кода только для реально используемых комбинаций типов
    /// Преимущества: нулевая стоимость абстракции, без накладных расходов в runtime
    /// Недостатки: бинарник может раздуться
    #[default]
    OnDemand,

    /// Полная мономорфизация — предварительная генерация всех возможных комбинаций типов
    /// Преимущества: все вызовы определены на этапе компиляции
    /// Недостатки: медленная компиляция, большой бинарник
    Full,
}

/// Конфигурация мономорфизации
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MonoConfig {
    /// Включена ли мономорфизация
    #[serde(default = "default_true")]
    pub enabled: bool,

    /// Стратегия мономорфизации
    #[serde(default)]
    pub strategy: MonoStrategy,

    /// Включено ли DCE (удаление мёртвого кода)
    #[serde(default = "default_true")]
    pub dce_enabled: bool,

    /// Максимальная глубина специализации (для предотвращения бесконечной рекурсии в обобщениях)
    #[serde(default = "default_max_mono_depth")]
    pub max_depth: usize,
}

impl Default for MonoConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            strategy: MonoStrategy::OnDemand,
            dce_enabled: true,
            max_depth: 100,
        }
    }
}

Интеграция в процесс компиляции

rust
// src/frontend/pipeline.rs

impl Pipeline {
    fn run_ir_generation(
        &mut self,
        source_name: &str,
        source: &str,
        ast: &Module,
        type_result: &TypeCheckResult,
        phase_durations: &mut Vec<(CompilationPhase, u64)>,
    ) -> IRResult {
        let start = Instant::now();

        // 1. Генерация базового IR
        let mut ir = middle::generate_ir(ast, type_result)?;

        // 2. Выполнение проходов оптимизации в соответствии с уровнем оптимизации
        let optimizer = Optimizer::for_opt_level(self.config.optimization_level);
        let pass_config = PassConfig {
            opt_level: self.config.optimization_level,
            debug_info: self.config.generate_debug_info,
            target_platform: TargetPlatform::detect(),
        };

        let result = optimizer.run(&mut ir, &pass_config);

        let duration = start.elapsed().as_millis() as u64;
        phase_durations.push((CompilationPhase::Optimization, duration));

        IRResult::success(result.module)
    }
}

Влияние на систему типов

Нет прямого влияния. Проходы оптимизации выполняются на уровне IR и не затрагивают систему типов.

Поведение во время выполнения

Уровень оптимизацииПоведение во время выполнения
O0Без оптимизации, сохранение всей отладочной информации
O1Базовая оптимизация, сохранение основной отладочной информации
O2Стандартная оптимизация, без отладочной информации
O3Агрессивная оптимизация, без отладочной информации

Ключевой момент: изменения в runtime не требуются. Проходы оптимизации влияют только на уровень IR и генерацию кода, runtime выполняет поиск и выполнение функций по имени/ID и не осведомлён о процессе оптимизации.

Изменения в компиляторе

КомпонентИзменение
frontend/config.rsДобавлены перечисление OptLevel и структура MonoConfig
frontend/pipeline.rsИнтеграция менеджера проходов
middle/passes/optimizer/Добавлен новый модуль проходов оптимизации
middle/passes/mono/Рефакторинг в стандартный интерфейс проходов
CLIДобавлен параметр --opt-level

Обратная совместимость

  • ✅ Полная обратная совместимость
  • Уровень оптимизации по умолчанию — O1, поведение соответствует текущему
  • Пользователь может явно указать уровень оптимизации для переопределения поведения по умолчанию

Компромиссы

Преимущества

  • Гибкость: пользователи могут выбирать стратегию оптимизации под свой сценарий
  • Расширяемость: стандартный интерфейс проходов упрощает добавление новых оптимизаций
  • Предсказуемость: чётко определено поведение каждого уровня оптимизации
  • Удобство отладки: режим O0 сохраняет полную отладочную информацию

Недостатки

  • Увеличение сложности: необходимо поддерживать несколько уровней оптимизации
  • Увеличение матрицы тестирования: требуется тестировать поведение на каждом уровне оптимизации
  • Документационная нагрузка: необходимо объяснять значение каждого уровня оптимизации

Альтернативные варианты

ВариантПричина отклонения
Только два состояния: вкл/выклНевозможность тонкой настройки глубины оптимизации
Использование стиля GCC/LLVM с цифрами -OНе соответствует конфигурационной системе YaoXiang
Независимое переключение каждого прохода оптимизацииПользователю требуется знание деталей каждого прохода, использование слишком сложное
Отложить до v2.0Мономорфизация уже реализована, но не интегрирована — сначала нужно решить архитектурные проблемы

Стратегия реализации

Разделение на этапы

  1. Этап 1 (текущий): Определение уровней оптимизации и интерфейса проходов
  2. Этап 2: Реализация прохода мономорфизации (на основе существующего модуля mono/)
  3. Этап 3: Реализация проходов свёртки констант и удаления мёртвого кода
  4. Этап 4: Реализация проходов встраивания функций и оптимизации хвостовых вызовов
  5. Этап 5: Реализация агрессивных проходов оптимизации (анализ побега, оптимизация циклов)

Зависимости

  • Зависит от RFC-011 (система обобщённых типов) — модуль мономорфизации
  • Зависит от RFC-028 (JIT-компилятор) — интерфейс проходов оптимизации
  • С RFC-018 (LLVM AOT)共享优化 Pass 设计 — общий дизайн проходов оптимизации

Риски

  • Регрессия производительности: проходы оптимизации могут внести ошибки, ведущие к снижению производительности
  • Увеличение времени компиляции: проходы оптимизации увеличивают время компиляции
  • Раздувание бинарного файла: мономорфизация может значительно увеличить размер бинарного файла

Открытые вопросы

  • [ ] Должен ли уровень O3 по умолчанию включать анализ побега? (@晨煦:需要 данные бенчмарков производительности)
  • [ ] Нужны ли уровни Os (оптимизация размера) и Oz (максимальная оптимизация размера)?
  • [ ] Должен ли уровень оптимизации влиять на степень детализации отладочной информации?
  • [ ] Как обрабатывать циклические зависимости между проходами оптимизации?

Приложение A: Журнал архитектурных решений

РешениеРешениеДатаЗаписал
Именование уровней оптимизацииИспользование O0-O3 + Auto2026-06-16晨煦
Уровень оптимизации по умолчаниюO1 (базовая оптимизация)2026-06-16晨煦
Стратегия мономорфизацииПоддержка Erased/OnDemand/Full2026-06-16晨煦
Дизайн интерфейса проходовtrait + объявление зависимостей2026-06-16晨煦

Приложение B: Глоссарий

ТерминОпределение
Проход оптимизацииНезависимый модуль, выполняющий одно преобразование IR
МономорфизацияСтратегия генерации кода, при которой обобщённые функции специализируются для конкретных типов
Свёртка константВычисление константных выражений на этапе компиляции
Удаление мёртвого кодаУдаление недостижимого или неиспользуемого кода
Встраивание функцийВставка тела функции в место вызова для избежания накладных расходов на вызов
Оптимизация хвостовых вызововПреобразование хвостовой рекурсии в цикл для избежания переполнения стека
Анализ побегаАнализ того, покидает ли переменная область видимости, для определения распределения в стеке/куче

Список литературы


Жизненный цикл и судьба

Данный RFC определяет архитектурный дизайн уровней оптимизации, закладывая единый фреймворк для будущих проходов оптимизации.

Связь с мономорфизацией: мономорфизация является одним из проходов оптимизации и будет реализована первой после принятия данного RFC.