Hot Reload в браузере: как мы ускорили компиляцию .NET с 84 секунд до 5

Сократить время компиляции .NET-проекта в браузере с 84 секунд до 5 — задача, которая казалась невыполнимой, но мы её решили. В нашей онлайн-IDE XAML.IO мы запустили реальный .NET-проект FamilyShow, содержащий десятки XAML-файлов, сотни C-файлов и множество зависимостей. Изначально простая правка од

Hot Reload в браузере: как мы ускорили компиляцию .NET с 84 секунд до 5

Сократить время компиляции .NET-проекта в браузере с 84 секунд до 5 — задача, которая казалась невыполнимой, но мы её решили. В нашей онлайн-IDE XAML.IO мы запустили реальный .NET-проект FamilyShow, содержащий десятки XAML-файлов, сотни C-файлов и множество зависимостей. Изначально простая правка одной строки в .cs-файле заставляла ждать больше минуты, что делало интерактивную разработку невозможной. Сейчас та же операция занимает 5 секунд, а изменение XAML-разметки на типичной странице — меньше секунды. В этой статье мы расскажем, как нам это удалось, какие технические преграды мы преодолели и какие сюрпризы нас поджидали.

Инкрементальная компиляция в браузере: от 84 секунд к 5

Ключевая проблема заключалась в том, что изначально мы компилировали весь проект целиком при каждом изменении. Roslyn, компилятор .NET, не был рассчитан на работу в браузере через WebAssembly, а OpenSilver — фреймворк для запуска WPF-приложений в браузере — добавлял свои накладные расходы. В итоге каждый цикл «правка → компиляция → запуск» занимал больше минуты, что делало интерактивный дизайн невозможным.

Решение пришло в виде инкрементальной компиляции. Мы разбили процесс на этапы: сначала анализируем изменения только в одном файле, перекомпилируем только его, а затем подменяем скомпилированный модуль в уже работающем приложении. Для этого пришлось модифицировать Roslyn, чтобы он поддерживал частичную компиляцию в WASM-окружении, и адаптировать OpenSilver для горячей замены сборок без перезагрузки страницы.

Почему инкрементальная компиляция .NET в браузере так сложна?

Традиционные IDE вроде Visual Studio решают проблему скорости с помощью фоновой компиляции и Hot Reload, но эти механизмы завязаны на нативную среду выполнения. В браузере, где код компилируется в WebAssembly, такие подходы не работают напрямую. Нам пришлось изобретать собственный велосипед, опираясь на внутренние механизмы Roslyn и особенности OpenSilver. Основная сложность — Roslyn в браузере работает через Mono WASM, и его производительность оставляет желать лучшего. Мы оптимизировали несколько узких мест: уменьшили количество аллокаций, отказались от некоторых фич Roslyn, которые не нужны в рантайме (например, генерация XML-документации), и заменили стандартный сборщик мусора на более эффективный в контексте WASM.

Предыстория и контекст

XAML.IO — это онлайн-IDE для .NET, где дизайн, компиляция и запуск приложений происходят прямо в браузере, без сервера. Проект существует уже несколько лет, и со временем мы научились загружать в него довольно крупные .NET-проекты. Однако чем больше становились проекты, тем острее ощущалась проблема скорости компиляции. Пользователи жаловались, что даже небольшие правки требуют длительного ожидания, и это снижало продуктивность.

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

Как работает инкрементальная компиляция в браузере?

Вместо того чтобы перекомпилировать весь проект, мы отслеживаем изменения в файлах с помощью файлового watcher'а. Когда пользователь правит один .cs-файл, мы запускаем компиляцию только этого файла с учётом его зависимостей. Roslyn позволяет получить синтаксическое дерево и семантическую модель для отдельного файла, но в браузере это работает нестабильно из-за ограничений памяти и времени выполнения WASM.

Мы обошли это, создав кэш синтаксических деревьев для всех файлов проекта и обновляя только изменённые. Затем Roslyn выполняет инкрементальную сборку: он переиспользует результаты предыдущей компиляции для неизменённых частей. В результате время сборки сократилось до времени, необходимого для компиляции одного файла плюс накладные расходы на линковку.

Технические подробности: как мы победили Roslyn, OpenSilver и WASM

Основная сложность была в том, что Roslyn в браузере работает через Mono WASM, и его производительность оставляет желать лучшего. Мы оптимизировали несколько узких мест: уменьшили количество аллокаций, отказались от некоторых фич Roslyn, которые не нужны в рантайме (например, генерация XML-документации), и заменили стандартный сборщик мусора на более эффективный в контексте WASM.

Кроме того, OpenSilver использует собственную систему типов и рефлексии, которая не поддерживает горячую замену сборок. Пришлось патчить OpenSilver: мы добавили возможность выгружать старую сборку и загружать новую без перезагрузки страницы. Для этого пришлось переписать часть рантайма, отвечающую за загрузку типов и разрешение зависимостей.

Ещё один сюрприз преподнёс WebAssembly: он не поддерживает динамическую загрузку кода в том виде, к которому мы привыкли в нативных приложениях. Каждая сборка — это отдельный WASM-модуль, и их нужно связывать через таблицы импорта. Мы реализовали собственный загрузчик, который умеет подменять функции в уже запущенном модуле.

Кого затронет и как

Разработчики, использующие онлайн-IDE для .NET, получат возможность работать с крупными проектами без длительного ожидания компиляции. Особенно это важно для тех, кто занимается WPF-разработкой: XAML.IO теперь позволяет редактировать XAML и C в режиме реального времени, видя результат сразу.

Для бизнеса это означает, что онлайн-среды разработки становятся полноценной заменой настольным IDE для .NET-проектов. Российские компании, которые ищут импортозамещение для Visual Studio, могут обратить внимание на XAML.IO как на облачное решение.

Конкуренты вроде GitHub Codespaces или Gitpod пока не предлагают полноценного Hot Reload для .NET в браузере, так что наше решение даёт XAML.IO значительное преимущество.

Что будет дальше

В ближайших планах — поддержка инкрементальной компиляции для проектов с несколькими целевыми фреймворками и оптимизация для больших проектов (сотни тысяч строк кода). Также мы работаем над тем, чтобы уменьшить время правки XAML до нуля: планируем внедрить предварительную компиляцию разметки в фоновом режиме.

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

Итог

Инкрементальная компиляция в браузере — сложная, но решаемая задача. Мы сократили время пересборки с 84 секунд до 5, сделав Hot Reload реальностью для .NET-проектов в онлайн-IDE. Это открывает путь к созданию полноценных визуальных дизайнеров в браузере, не уступающих настольным аналогам. Следите за обновлениями XAML.IO — мы только начинаем.