Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Chroix — optimization research (raw agent output, 2026-10-03)

Источник: dev/fera/chroix-project/CHROIX-OPTIM-RESEARCH.md (read-only; сгенерировано scripts/sync-docs.mjs)

Chroix — optimization research (raw agent output, 2026-10-03)

Section titled “Chroix — optimization research (raw agent output, 2026-10-03)”

Five parallel research agents (Opus). Raw structured output, to be distilled into V2 proposals.

  • idea: proc-macro view! парсит дерево виджетов и генерит статический дескриптор: static TEMPLATE: Template { roots: &'static [TemplateNode], dyn_paths: &'static [&'static [u8]] }. Статические поддеревья — это данные в rodata, динамика — только «дыры» (индексы сигналов/выражений). Диффинг статики = сравнение указателей, рантайм ходит только по дырам.
  • runtimeGain: Нулевые аллокации VDOM на статических поддеревьях (в чате — 90%+ дерева: аватарки, бабблы, разметка); diff O(число динамических узлов) вместо O(дерево); дешёвый re-render при смене сигнала — пересоздаётся только дыра.
  • compileTimeCost: Средняя: полный syn-парс rsx (включает syn/full) — +2–5 с на proc-macro крейт один раз, дальше линейно по объёму макросов. Митигируется venial/unsynn вместо syn.
  • tradeoffs: Нужен свой парсер DSL внутри proc-macro (поддержка if/for в разметке усложняет схему путей); hot-reload разметки требует повторного парсинга исходника (как dioxus-hot-reload); дебаг-вывод macro expansion нужен сразу (cargo-expand).
  • existingUsage: Dioxus (rsx! → Template, с 0.4; dioxus-hot-reload парсит rsx текстуально), Leptos (view! → typed builders; SSR static segments), Slint (компилирует весь .slint). Prior art вне Rust: Vue 3 static hoisting + block tree, Solid — compiled statements без VDOM.
  • rustFit: Идеально: Template — это ’static данные, свободно пересекают границу fload-DLL как данные (код меняется при свопе, данные-описания нет). Хорошо ложится на решение «нет runtime tracking»: список дыр известен при компиляции.

Dirty bitmask codegen (Svelte/Compose-style)

Section titled “Dirty bitmask codegen (Svelte/Compose-style)”
  • idea: Макрос при парсинге тела view/update собирает, какие сигналы читаются (через syn visitor по .read()/.get()), присваивает каждому сигналу бит в u64/u128 и генерит: if dirty & MASK_X != 0 { ... } вокруг блоков обновления. Никакого runtime-графа подписок — только маски.
  • runtimeGain: Инвалидация за 1-2 инструкции на проверку; нет HashMap<SignalId, Vec> и его аллокаций/кэш-миссов; отлично дружит с frame budget — проверка 1000 масок дешевле одного кэш-промаха на графе.
  • compileTimeCost: Низкая: один syn::visit::Visit по телу функции, генерация масок — тривиальный emit. Самая дешёвая техника из списка на единицу выигрыша.
  • tradeoffs: Лимит 64/128 сигналов на скоуп (далее — битсет из нескольких слов, усложнение codegen); динамические зависимости (чтение сигнала по вычисленному ключу) выпадают из схемы — для них нужен рантайм-фолбэк; переименование API чтения ломает visitor.
  • existingUsage: Svelte ($$.dirty bitmask — канонический пример), Kotlin Compose compiler (генерит параметр $changed: Int + skippability по stability типов), Relm4 (view! макрос генерит вотчеры только на реально используемые поля модели). В Rust-фреймворках полноценно не сделано — свободная ниша.
  • rustFit: Отлично: маски — Plain Old Data, пролетают через DLL-границу без ABI-проблем; прямо реализует уже принятое решение «compiled signal graph, нет runtime tracking».
  • idea: По результатам анализа чтений/записей (см. bitmask + update codegen) макрос на корне приложения (app!{}) генерит static EDGES: &[(u32,u32)], топологический порядок обхода и таблицу «сигнал → список грязных масок». Планировщик кадра — тупой цикл по таблице, граф не строится и не хранится в куче.
  • runtimeGain: Планирование кадра без аллокаций и без обхода графа указателей — плоский массив, prefetch-friendly; детерминированный порядок обновления; удаление целых классов багов «гонка подписок».
  • compileTimeCost: Низкая-средняя по времени компиляции, высокая по сложности авторства макроса: анализ в пределах одного макро-вызова прост, межфункциональный — требует собирать весь граф в корневом app!{}.
  • tradeoffs: Циклические зависимости обнаруживаются только если весь граф виден одному макросу — динамические плагины через fload не попадут в таблицу (для них нужен runtime-сегмент графа или запрет реактивности через границу); большие таблицы раздувают rodata (незначительно).
  • existingUsage: Svelte (топологическая сортировка $: блоков при компиляции), Slint (property binding graph резолвится компилятором, включая two-way bindings), Vue 3 compiler. Контраст: bevy schedule и leptos/dioxus-signals строят граф в рантайме.
  • rustFit: Очень хорошо: таблицы — данные, шарятся между DLL; но межвиджетные зависимости требуют единой точки сбора (корневой макрос), что согласуется с Elm-outside: единый update loop = единая таблица.

Layout specialization — код вместо интерпретатора

Section titled “Layout specialization — код вместо интерпретатора”
  • idea: Для статически известного layout (фиксированные ряды/колонки, константные отступы, известные пропорции) макрос генерит прямой код вычисления Rect: r.y = header_h + i * row_h вместо обхода дерева интерпретатором (taffy-стиль). Интерпретатор остаётся только для действительно динамических поддеревьев (список сообщений переменной высоты).
  • runtimeGain: Убирает обход дерева, кэш constraint’ов и виртуальные вызовы measure на статическом большинстве UI; вычисления скалярные и SIMD-уязвимые; layout статического экрана — десятки инструкций вместо тысяч.
  • compileTimeCost: Средняя: объём сгенерированного кода растёт (мономорфизация layout-функций), при codegen-units=1 ощутимо на больших приложениях. Делить: специализация только для помеченных layout!{}-узлов.
  • tradeoffs: Макросу нужна собственная модель layout-системы (риск рассинхрона с рантайм-интерпретатором — нужен тест «spec == interp» на одних входах); анимированные/резиновые layout всё равно требуют интерпретатора; рост бинаря.
  • existingUsage: Slint (генерит код layout из .slint, включая вложенные формулы), const-generic compile-time layout редко, но taffy/кайя (interpreter) — то, что заменяем. Prior art: compile-time layout в Dear ImGui-стиле статических панелей, частично Clay (но Clay интерпретатор).
  • rustFit: Хорошо: сгенерированные fn мономорфизируются и инлайнятся при codegen-units=1; на границе DLL layout-функция вызывается по указателю из дескриптора — ABI-простая (constraints in, rect out).
  • idea: easing-кривые, решение cubic-bezier (итерации Ньютона), коэффициенты пружин, цветовые рампы (oklch-интерполяция темы) считаются в const fn и материализуются как static EASE_OUT_CUBIC: [f32; 257]. Кадр анимации = lookup + lerp.
  • runtimeGain: Ноль float-математики на кадр на кривую; детерминизм между платформами (нет libm-дрейфа — важно для snapshot-тестов headless-рендера); таблицы лежат в rodata, общие для всех анимаций.
  • compileTimeCost: Ничтожная: const fn с float-циклами — миллисекунды на таблицу. Ограничение — const eval budget для огромных таблиц (лимитируется, поднимается фичей/переносом в build.rs).
  • tradeoffs: Параметризованные кривые (пружина с произвольным stiffness/damping) не сводятся к одной таблице — либо сетка LUT по дискретным параметрам, либо рантайм-расчёт для нестандартных; точность таблицы vs плавность — подбирать шаг.
  • existingUsage: Классика геймдева (LUT для easing/springs), const-fnv1a-hash (const-хэши в match), palette (частично const fn), glam (const fn векторная математика). Compose/SwiftUI считают easing в рантайме — тут Rust может быть строго лучше.
  • rustFit: Идеально: f32-арифметика в const fn стабильна с Rust 1.82; LUT — данные, тривиально живут за DLL-границей; headless-рендер и Vello-рендер видят одни и те же значения (важно для пары Vello+headless).

build.rs кодогенерация схемы display list

Section titled “build.rs кодогенерация схемы display list”
  • idea: Операции display list (rects, glyphs runs из Parley, clips, transforms, layer push/pop) описываются в схеме (flatbuffers .fbs или rkyv-derive структуры); build.rs генерит encode/decode, общий для UI-крейта и рендер-крейта. Граница fload передаёт буфер, а не repr(C) структуры.
  • runtimeGain: Zero-copy: рендерер читает команды прямо из буфера UI-потока (rkyv/flatbuffers), нет сериализации/десериализации на кадр; схема версионируется — старый рендерер может отвергнуть новый список явно.
  • compileTimeCost: Разовая: prost-build/flatc/rkyv-derive отрабатывают в build.rs и кэшируются; инкрементально перегенерация только при смене схемы. Дисциплина: схема — отдельный крейт, чтобы обе стороны DLL собирались из одного источника.
  • tradeoffs: Flatbuffers требует builder-стиля записи (неудобнее struct literals); rkyv привязывает к версии rustc на обеих сторонах (для hot-swap dylib это и так требование); схема требует дисциплины эволюции (удалять поля нельзя).
  • existingUsage: prost-build/tonic (protobuf codegen в build.rs), flatbuffers (zero-copy, версионированная схема), rkyv (архивные типы с определённым layout), zerocopy (Fuchsia), capnp. Для DLL-плагинов: stabby, abi_stable — та же задача «без repr(C)».
  • rustFit: Сильно: прямо реализует «fload без repr(C)» — rkyv archived types имеют гарантированный layout при одинаковом rustc; flatbuffers выдерживает даже разные версии компилятора. Headless-бэкенд потребляет тот же буфер — один контракт на обе реализации.
  • idea: Макрос select!(state => .chats[id].unread_count) генерит линзу (путь в state) + memo-узел, чьи зависимости на этапе компиляции попадают в dep-таблицу и dirty-маску. Пересчёт derived-значения запускается только когда поднят соответствующий бит.
  • runtimeGain: Мемоизация без сравнения замыканий и без runtime-графа; триггер пересчёта O(1) через маску; selector equality (skip если результат не изменился) генерится с PartialEq-шорткатом.
  • compileTimeCost: Низкая: парсинг path-выражения — маленький syn-visitor; эмит линз механический. Дороже становится при вложенных вычислимых ключах (индексы по сигналу — запретить или рантайм-фолбэк).
  • tradeoffs: Вычислимые ключи (id из сигнала) ломают статический анализ — нужен escape hatch select_dyn! с рантайм-подпиской; линзы через Option/Vec индексации требуют сгенерированного кода с проверками, ошибки должны быть читаемыми (care про diagnostics).
  • existingUsage: Zustand/Recoil selectors (prior art, JS), leptos create_memo/create_selector (рантайм — контраст), dioxus-signals memo (рантайм), lens-rs (Rust-макрос линз). Svelte 5 $derived — компилируемый derived, ближайший аналог.
  • rustFit: Хорошо: селекторы — чистые fn над &State, естественны для Elm-outside (один Model, линзы в него); compose с bitmask-техникой даёт полный fine-grained слой без runtime tracking.

Type-level view trees / мономорфизированный dispatch

Section titled “Type-level view trees / мономорфизированный dispatch”
  • idea: Композиция виджетов как вложенные дженерики (Column<Label, List<Msg, Bubble>>) с tuple-имплементациями трейта View до N (как xilem/Bevy). Никаких trait objects на горячем пути — весь dispatch статический, дерево типов = дерево виджетов.
  • runtimeGain: Полная девиртуализация и инлайнинг measure/layout/draw на статических ветках; PGO видит конкретный call graph; нулевая стоимость композиции обёрток (Padding<Align> исчезает).
  • compileTimeCost: ВЫСОКАЯ, максимальная в списке: взрыв мономорфизации × codegen-units=1 × LTO — именно это делает bevy/xilem-разработку мучительной. Митигировать: двухпрофильная сборка (dev: dynamic linking / bevy_dylib-стиль, release: CU=1+LTO+PGO+BOLT), #[inline] только на доказанно горячем, type-erased Element на границах редко меняющихся поддеревьев.
  • tradeoffs: Compile time и размер бинаря; сообщения об ошибках на глубоких типах чудовищны (нужны static_assertions::assert_impl_all, derive-where, ручные type alias’ы); recursion_limit; рефакторинг типа дерева = рефакторинг всех сигнатур.
  • existingUsage: Xilem (тип дерева = его форма, tuple-имплементации View до N, Memoize), Bevy (tuple SystemParam/WorldQuery до 15, общеизвестно тяжёлые сборки), sycamore (generic views). Контраст: iced стирает типы в Element (dyn) — платит рантаймом.
  • rustFit: По runtime — отлично, но есть конфликт с hot-swap: мономорфизированный код живёт в той dylib, где инстанциирован; виджеты, собранные в app-DLL, нельзя свопнуть по одному. Решение: мономорфизация внутри DLL, через границу — dyn за fload-vtable (nylon-ring).

Enum dispatch для встроенных виджетов

Section titled “Enum dispatch для встроенных виджетов”
  • idea: Замкнутое множество встроенных виджетов приложения собирается макросом в один enum Widget { Label(..), Button(..), … } с сгенерированными match-имплементациями трейтов (enum_dispatch или свой макрос). dyn остаётся только на границе плагинов.
  • runtimeGain: Dispatch через match — предсказуемый переход + возможность инлайна мелких методов, против непредсказуемого indirect call через vtable; меньше указателей на vtable в памяти виджета.
  • compileTimeCost: Низкая: enum_dispatch — лёгкий derive без полного syn-парсинга чужих типов; центральный enum пересобирает только свой крейт при добавлении виджета (а это и есть свопаемый app-DLL — даже плюс).
  • tradeoffs: Добавление виджета = правка центрального enum (для closed world — норм); размер enum = размер максимального варианта (большие виджеты — Box внутри варианта); не подходит для открытого плагинного набора.
  • existingUsage: enum_dispatch (crate, бенчи ~10x против dyn на мелких методах), derive_more, подход «closed set enum» внутри tokio/smol и рендереров. Slint генерит enum item’ов из .slint.
  • rustFit: Сильно: внутренние виджеты живут в одном DLL — enum там же; плагины через fload — trait object на границе, внутри границы enum. Не конфликтует с type-level деревьями: enum — для листьев/примитивов.

Typestate/compile-time FSM для виджетов

Section titled “Typestate/compile-time FSM для виджетов”
  • idea: Жизненные циклы виджетов (загрузка истории чата: Idle→Loading→Loaded→Failed) и инварианты формы графа (арность связей, «эффект не читает то, что пишет») проверяются типами/макросом при компиляции; переходы генерятся исчерпывающим match.
  • runtimeGain: Исчезают рантайм-проверки состояния и unreachable!-ветки; переходы — branchless по таблице; компилятор выкидывает мёртвые состояния.
  • compileTimeCost: Средняя: DSL-парсинг маленький, но typestate раздувает trait resolution и ухудшает ошибки; rust-fsm-подход (макрос генерит обычный enum + match) дешевле для компилятора, чем чистые typestate-типы.
  • tradeoffs: Typestate раздувает сигнатуры и ошибки компилятора; не каждый инвариант выражаем; риск переинжинирить — брать только для 2-3 реально болезненных автоматов (загрузка, reconnect, auth).
  • existingUsage: rust-fsm (DSL → compile-time проверенный FSM), statig, smlang, typestate-паттерн в hyper/embedded-hal. Bevy States — рантайм (контраст). Для графов сигналов аналогов нет — ниша.
  • rustFit: Хорошо для per-widget FSM; честно: полную ацикличность сигнального графа типами НЕ проверить без чудовищных typenum-конструкций — ограничить типами локальные инварианты, граф валидировать в корневом макросе (техника 3) с человекочитаемой ошибкой.

PHF/const-hash интернинг идентификаторов

Section titled “PHF/const-hash интернинг идентификаторов”
  • idea: Имена классов стилей, id виджетов, строковые ключи сообщений — static MAP: phf::Map<&'static str, u16> или match по const fn fnv1a(s). Никаких HashMap/SipHash на горячем пути lookup’ов.
  • runtimeGain: O(1) lookup с идеальным хэшем без рантайм-таблиц и их инициализации; сравнение по u16-id вместо строк везде ниже по стеку (стили, события, dispatch).
  • compileTimeCost: Ничтожная: phf_macros и const fn хэши — копеечны. Осторожно только с объёмом: тысячи строк в одной phf-таблице ощутимы при парсинге макроса.
  • tradeoffs: Ключи должны быть известны при компиляции — пользовательские/плагинные строки требуют рантайм-интернера рядом (двухуровневая схема: phf для ядра, interner для плагинов); коллизии const-fnv в match надо контролировать (assert на этапе макроса).
  • existingUsage: phf/phf_macros, Servo string_cache (interning атомов в движке), const-fnv1a-hash (const-хэш в match-константах), string-interner (рантайм — контраст).
  • rustFit: Идеально: статики, безопасны через DLL-границу; id вместо строк — ещё и ABI-упрощение для fload-сообщений.
  • idea: Иконочные атласы, MSDF/SDF-глyфы, предпарсенные темы (палитры уже в линейном RGB), локали — запекаются build.rs в байты и подключаются через include_bytes!/include_dir. Рантайм не содержит парсеров SVG/ttf/локалей.
  • runtimeGain: Старт без парсинга и растеризации; меньше рантайм-зависимостей (и их кода в бинаре); предсказуемый frame 1 — атлас уже в нужном формате для Vello.
  • compileTimeCost: Разовая в build.rs (MSDF-генерация может быть секунды на шрифт — кэшировать в target/ и пропускать по хэшу входов); рантайм-экономия на каждом старте.
  • tradeoffs: Рост артефактов сборки; инкрементальность build.rs требует дисциплины (rerun-if-changed); правка ассета = пересборка куска пайплайна.
  • existingUsage: rust-embed, include_dir, include_bytes!, типичные game pipeline’ы (baked atlases), vergen-стиль кодогенерации метаданных. msdfgen/atlasgen тулчейны — из геймдева.
  • rustFit: Хорошо, с оговоркой: запечённые ассеты внутри swappable dylib раздувают payload хот-свапа. Решение: ассеты — отдельный data-файл или отдельный НЕсвопаемый крейт/DLL, свопаемый UI шлёпает только пути/handle’ы.

Codegen сообщений и write-masks (Elm-outside)

Section titled “Codegen сообщений и write-masks (Elm-outside)”
  • idea: Derive/макрос над enum Msg: генерит диспатч-таблицу хендлеров, а из анализа тел update-хендлеров (какие сигналы пишутся) — write-mask на вариант сообщения. Планировщик: обработал Msg::X → поднял биты WRITE_MASK[X] → dirty-проверки по маскам (техника 2).
  • runtimeGain: Инвалидация после update — без diff’а состояния и без «погаси всё»: точные биты записанных сигналов; dispatch сообщений — таблица fn-указателей/прямой match, исчерпанность проверена компилятором.
  • compileTimeCost: Низкая-средняя: derive по enum Msg — стандартный syn-derive; таблица записей требует видеть тела хендлеров — либо они в том же макро (update!{}), либо аннотации #[writes(..)] с ручной дисциплиной.
  • tradeoffs: Write-анализ хрупок к косвенным записям (через &mut переданный дальше) — нужны либо аннотации, либо консервативный fallback (маска «всё» для неанализируемых хендлеров); ложные биты = лишняя перерисовка, не баг — деградация мягкая.
  • existingUsage: Elm compiler (эффекты/сообщения — канон), Relm4 (макрос связывает поля модели и виджеты), iced (рантайм-сообщения — контраст), enum-map/enum_dispatch для таблиц. Svelte-компилятор — аналог для write-анализа.
  • rustFit: Очень сильно: это прямое усиление уже выбранного Elm-outside; Msg enum и есть естественная граница сообщений для fload (enum + schema-кодогенерация = сериализуемые события через DLL).

Лёгкая макро-инфраструктура (анти-техника compile time)

Section titled “Лёгкая макро-инфраструктура (анти-техника compile time)”
  • idea: Инфраструктурные решения, чтобы макро-тяжёлая стратегия не убила итерацию: venial/unsynn вместо полного syn там, где грамматика простая; отдельный proc-macro крейт, собираемый один раз, а свопаемые DLL зависят только от его выхода через тонкий runtime-крейт; workspace-сплит; sccache + rust-lld; cargo-expand/insta/trybuild как обязательный тулчейн макросов.
  • runtimeGain: Косвенный: позволяет держать в релизе codegen-units=1+LTO и при этом итерировать; dev-профиль с dynamic linking даёт секундный цикл правки UI-DLL.
  • compileTimeCost: Отрицательная (это техника СНИЖЕНИЯ compile time): venial/unsynn режут стоимость proc-macro в разы против syn/full; sccache+rust-lld — константные секунды на линковке свопаемых DLL.
  • tradeoffs: venial/unsynn покрывают не весь синтаксис (сложные generics придётся докручивать); поддержка своего тулчейна макросов — постоянная статья работ; двухпрофильная сборка требует дисциплины CI (проверять оба профиля).
  • existingUsage: venial (godot-rust/gdext — крупный proc-macro проект на нём), unsynn, paste, seq-macro, derive-where, static_assertions, trybuild, insta, cargo-expand, cargo-hakari (feature unification), sccache. Bevy — dynamic linking feature (bevy_dylib) как эталон митигации для dev-профиля.
  • rustFit: Обязательна для Chroix: hot-swap сам по себе — инструмент быстрой итерации, и он должен быть зарезервирован под runtime-перезагрузку кода, а не съедаться монструозными пересборками макросов.

Compile-time feature/ABI gates для hot-swap

Section titled “Compile-time feature/ABI gates для hot-swap”
  • idea: Компилятором сгенерированные assert’ы: const-eval проверки размеров/смещений ключевых структур display list, версии схемы и feature-флаги вшиваются в обе стороны; при загрузке DLL — handshake по сгенерированной таблице версий. Любой рассинхрон — отказ загрузки, а не UB.
  • runtimeGain: Нуль на горячем пути; экономия — отсутствие охоты за UB при несовпадении версий DLL (самый дорогой класс багов hot-swap).
  • compileTimeCost: Средняя: build.rs скачивает/проверяет версии, тесты медленнее. Снимает целый класс «тихих» багов ABI при частых свопах.
  • tradeoffs: Нужно версионирование всего, что пересекает границу; ложные отказы при неосторожном бампе версий раздражают.
  • existingUsage: guppy/hakari (determinism фичей), vergen (версии в бинарь), cargo-deny, flatbuffers reflection (схема в рантайме), stabby (проверки layout при загрузке).
  • rustFit: Прямо обслуживает fload/nylon-ring модель: без repr(C) рассинхрон layout — главный риск, и его надо гасить сгенерированными проверками, а не дисциплиной.

Notes: Ключевые ссылки и крейты: dioxus-core (Template statics), slint-build, leptos_hot_reload, relm4-macros (view! watchers), enum_dispatch, stabby/abi_stable (контраст для fload/nylon-ring), rkyv/flatbuffers/prost-build/zerocopy, phf/phf_macros, const-fnv1a-hash, venial (godot-rust gdext), unsynn, paste, seq-macro, derive-where, static_assertions, trybuild, cargo-expand, insta, rust-fsm/statig/smlang, lens-rs, rust-embed/include_dir, linkme/inventory. Cross-language prior art: Svelte ($$.dirty bitmask + topo-sorted реактивность), Vue 3 (patch flags + block tree + static hoisting), Solid (compiled statements вместо VDOM), Compose compiler plugin ($changed:Int bitmask, stability inference, skippability) — Compose это самый близкий аналог «flat dependency tables на уровне компилятора». Приоритет для Chroix: техники 1+2+3+11 — это ОДНА когерентная макро-система (view! парсит тело, извлекает чтения сигналов, генерит Template + dirty-mask + dep table + write-mask хендлеров); делать вместе, не по отдельности. 6 (схема display list) — обязательна из-за fload, не опция. 7 — дешёвая победа, делать сразу. 5 — высший runtime-выигрыш но худший compile time и конфликт гранулярности с hot-swap: мономорфизированный код живёт внутри dylib, где определён; применять внутри одного DLL-модуля, не через границу. Предостережения: linkme/inventory НЕ работают через границу dylib (у каждой DLL своя копия distributed slice) — для плагинов нужна явная регистрация при загрузке; asset baking внутрь swappable dylib раздувает payload хот-свапа — ассеты в отдельный файл/несвопаемый крейт; с codegen-units=1 + LTO тяжёлая мономорфизация убьёт итерацию — митигировать двухпрофильной сборкой (dev: dynamic linking как bevy_dylib, release: CU=1+LTO+PGO+BOLT). Смежное (не compile-time, но из той же оперы): warm-start через rkyv-снапшот инициализированного состояния + mmap — ускоряет старт, отдельная техника.

Static pointer table через exported symbol

Section titled “Static pointer table через exported symbol”
  • idea: Одиночный #[no_mangle] pub static PLUGIN_API: PluginApi в DLL, где PluginApi — flat struct из extern fn pointers. Host один раз резолвит через GetProcAddress и дальше вызывает напрямую. Никакого vtable, никакого dyn — просто адрес функции.
  • runtimeGain: Zero-cost dispatch: одна индирекция через статический адрес, компилятор может инлайнить через PGO/BOLT. Полностью соответствует уже решённому ‘link-time dispatch без vtables’.
  • compileTimeCost: Низкая — обычные extern fn и static
  • tradeoffs: Все поля обязаны быть заполнены; добавление нового поля — append-only в конец struct, иначе layout ломается.
  • existingUsage: Hotline (live coding Rust engine), many game engines, pluggy-подобные проекты, classic Win32 plugins
  • rustFit: Идеально. Уже по сути является nylon-ring-моделью. На Windows требует #[no_mangle] pub static + dllimport корректно, с libloading работает из коробки.

Versioned struct envelope (sType/pNext pattern)

Section titled “Versioned struct envelope (sType/pNext pattern)”
  • idea: Каждая repr(C) структура начинается с sType: u32 (enum-магик) и pNext: *const c_void. Расширения — это цепочка структур через pNext. Host обходит цепочку, резолвит только те sType, которые знает.
  • runtimeGain: Позволяет эволюционировать ABI без ломки: старый DLL видит знакомые sType, новые игнорирует. Проверка версии O(1) на загрузке.
  • compileTimeCost: Средняя — макросы для chain-резолва
  • tradeoffs: Traversal цепочки на каждой инициализации; typed-обёртка требует unsafe для cast’ов.
  • existingUsage: Vulkan (VkInstanceCreateInfo, VkDeviceCreateInfo, все extension-структуры), OpenXR, DirectX 12 (через D3D12_FEATURE_DATA), WebGPU native
  • rustFit: Хорошо. Через #[repr(C)] + *const c_void и макрос для typed traversal. Немного boilerplate, но никаких vtables.
  • idea: build.rs проходит по всем repr(C) типам, вычисляет хеш от (имя, offsets, sizes, alignments) и встраивает в DLL как pub static ABI_FINGERPRINT: u64. Host после LoadLibrary читает этот символ и сверяет со своим — при расхождении отказывается грузить или откатывается.
  • runtimeGain: Убирает целый класс UB при hot-swap: layout-несовместимость обнаруживается на загрузке, а не на разыменовании.
  • compileTimeCost: Средняя — build.rs + proc-macro для сбора layout
  • tradeoffs: Ложные срабатывания при refactoring (переименование приватного поля меняет хеш); нужна allowlist-политика.
  • existingUsage: Fuchsia FIDL (ordinal hashing), Cap’n Proto schema evolution, некоторые embedded RTOS (закрытые), cbindgen + diff в CI у Parity/Wasmtime
  • rustFit: Очень хорошо. Можно начать с cbindgen + текстового diff в CI, потом перейти на полноценный fingerprint через syn/proc-macro.
  • idea: Любая repr(C) структура имеет первое поле struct_size: u32. Host при чтении смотрит struct_size и знает, какая версия layout пришла. Дальше — match по версии. Append-only правило: новые поля только в конец.
  • runtimeGain: Версионирование почти бесплатно; позволяет держать несколько версий DLL одновременно.
  • compileTimeCost: Средняя — генерация кода
  • tradeoffs: Не ловит изменение типа существующего поля; требует дисциплины ‘только append’.
  • existingUsage: Windows Runtime (WinRT), некоторые plugin-системы; существует неофициальный паттерн в社区
  • rustFit: Хорошо. Просто дисциплина + один #[repr(C)] header.
  • idea: Host держит две загруженные версии DLL: current и next. На границе кадра, когда весь UI-thread code гарантированно не исполняет DLL-код (quiescent point), host переключает atomic pointer на next, а старую выгружает. Никакого EBR/HP — естественная quiescence от frame boundary.
  • runtimeGain: Hot-swap за O(1) на границе кадра; никаких locks, никаких hazard pointers. Под frame budget 16ms помещается тривиально.
  • compileTimeCost: Низкая — одна atomic-операция на фрейм
  • tradeoffs: Требует, чтобы DLL не держала long-lived pointers в host state через кадровую границу; state serialization или внешнее хранение state (в host) обязательны.
  • existingUsage: Hotline, Unreal live coding (через serialization), некоторые audio plugin hosts (VST3 reload), Emacs dynamic modules (через unload-safety protocol)
  • rustFit: Отлично. Rust + libloading позволяет Library::drop, но нужно гарантировать, что нет outstanding references — frame barrier это гарантирует.

EBR / hazard pointers для unload-safety

Section titled “EBR / hazard pointers для unload-safety”
  • idea: Классический EBR (crossbeam-epoch) или hazard pointers: читатели регистрируют активные указатели, освобождение откладывается до тех пор, пока все читатели не выйдут из критической секции.
  • runtimeGain: Позволяет hot-swap без явной границы кадра — unload когда безопасно.
  • compileTimeCost: Высокая — unsafe обёртки, Send/Sync маркеры
  • tradeoffs: Сложная memory model; crossbeam-epoch имеет известные subtleties; hazard pointers требуют bounded количество читателей.
  • existingUsage: Folly hazard pointers (Meta), crossbeam-epoch, hyaline, seizе, — все для Rust; C++: folly, absl, libcds
  • rustFit: Можно, но для Chroix — overkill. UI-фрейм имеет естественную точку quiescence каждые 16ms, и двойной буфер + frame barrier даёт те же гарантии за 1% сложности. Использовать EBR только если появятся фоновые треды, долгоживущие в DLL-коде.

Shared memory ring buffer через memmap

Section titled “Shared memory ring buffer через memmap”
  • idea: Создать file-mapping (CreateFileMapping/MapViewOfFile на Windows) между host и DLL как один большой arena. Host пишет display list / event queue туда, DLL читает без copy. Ring buffer поверх с head/tail в разных cache lines.
  • runtimeGain: Zero-copy между host и DLL; на Windows это один syscall на setup, дальше — обычные load/store. Latency порядка наносекунд.
  • compileTimeCost: Низкая — bipbuffer/shmem-ipc готовые
  • tradeoffs: DLL и host делят одно адресное пространство фактически, но mapping может иметь разные базовые адреса — нужны относительные указатели (offset-based, не raw pointers). Это требует дисциплины.
  • existingUsage: shared_memory crate, shmem-ipc, raw_sync, iceoryx (C++ but with Rust bindings), Chromium Mojo shared buffers
  • rustFit: Хорошо. memmap2 + raw_sync (для shared mutexes) + bipbuffer (для ring). Всё есть на crates.io.
  • idea: Однопродюсер-одноконсьюмер lock-free ring buffer с bipartite layout (два contiguous сегмента для wrap-around). Producer пишет display list chunks, consumer читает.
  • runtimeGain: Lock-free, wait-free для producer; один atomic load/store на операцию; ~5-10ns per op.
  • compileTimeCost: Низкая — готовые крейты
  • tradeoffs: Bounded size — нужно выбрать capacity; переполнение = drop frame или блокировка.
  • existingUsage: bipbuffer crate, bbqueue (для embedded, но подходит), crossbeam-queue::ArrayQueue (bounded), rigtorp’s SPSCQueue (C++)
  • rustFit: Идеально для UI pipeline: UI thread производит команды, render thread потребляет. bipbuffer покрывает 95% случаев.
  • idea: Все hot поля (head/tail ring buffer, signal dirty flags, atomic counters) оборачиваются в CachePadded<T> (или ручной #[repr(align(64))]), чтобы не делить cache line с соседними полями.
  • runtimeGain: Убирает false sharing — может давать 2-10x на contended atomics. На 60fps петле это прямые микросекунды.
  • compileTimeCost: Низкая
  • tradeoffs: Трата 64-128 байт на поле; нужно применять осознанно, а не везде.
  • existingUsage: crossbeam-utils::CachePadded — используется везде в crossbeam, tokio, rayon
  • rustFit: Тривиально. crossbeam-utils::CachePadded уже стабилен.
  • idea: Все указатели в shared region хранить как u32/u64 offset от базы mapping. При доступе: base + offset. Это делает layout position-independent и работает корректно, даже если host и DLL имеют mapping по разным адресам.
  • runtimeGain: Позволяет share memory между процессами/DLL с разными base addresses; упрощает serialization.
  • compileTimeCost: Низкая
  • tradeoffs: Каждый dereference — одна дополнительная аддитивная операция (компилятор обычно её сливает); типобезопасность страдает без обёрток.
  • existingUsage: serde-arena, memmap2-backed stores в tantivy, rkyv (zero-copy deserialization)
  • rustFit: Хорошо, но требует дисциплины: никаких raw pointers в repr(C) структурах, которые идут в shared memory. rkyv этот паттерн формализует.
  • idea: Выделять всю frame-specific data (display list, layout results, текст-шейпы) в bump arena. На конец кадра — одним memset или просто reset pointer. Никаких individual drop.
  • runtimeGain: O(1) allocation (pointer bump), O(1) deallocation (reset). Убирает allocator pressure на hot path.
  • compileTimeCost: Низкая — bumpalo готовый
  • tradeoffs: Требует, чтобы DLL не хранила references в arena через кадровую границу; нужен явный контракт ‘frame-scoped’.
  • existingUsage: bumpalo crate, typed-arena, rustc itself использует arena в queries, Bevy uses bump allocator для frame data
  • rustFit: Идеально. bumpalo::Bump известен и проверен. Можно прокидывать через DLL boundary как raw pointer + capacity, потому что arena живёт в host.
  • idea: Стандартный libloading::Library для загрузки DLL, но все symbol resolutions кешируются на старте в struct of fn pointers. Никаких повторных GetProcAddress на hot path.
  • runtimeGain: GetProcAddress — syscall-ish, дорого. Pre-resolve превращает это в один setup cost.
  • compileTimeCost: Низкая
  • tradeoffs: Никаких — это просто best practice.
  • existingUsage: libloading::Library + Symbol, почти все Rust plugin-системы: bevy_dynamic_plugin, vizia, cargo-expand, mlua для Lua plugins
  • rustFit: Идеально, уже baseline для nylon-ring.
  • idea: Крейты, которые дают stable ABI для Rust-типов (Vec, String, Option, Box) через FFI, версионируют vtables автоматически.
  • runtimeGain: Позволяет шарить Rust-типы через DLL boundary без ручной repr(C) дисциплины.
  • compileTimeCost: Высокая (тяжёлые макросы, свой runtime)
  • tradeoffs: Vtables (конфликт с уже принятым решением), тяжёлые, compile-time дорого, своя экосистема.
  • existingUsage: abi_stable использовался в sauron (web framework, abandoned), некоторых старых plugin-системах; stabby активно развивается (ZettaScale), но в production широкого применения не видно
  • rustFit: Плохо для Chroix. Уже решено ‘link-time dispatch без vtables’ — abi_stable/stabby построены на vtables внутри. Плюс тянут runtime, заметный compile time, и не дают ничего, что ручная repr(C) дисциплина не даёт дешевле. НЕ брать.

NUMA-aware allocation (VirtualAlloc2 + preferred node)

Section titled “NUMA-aware allocation (VirtualAlloc2 + preferred node)”
  • idea: Привязка memory allocation к NUMA node через специальные API.
  • runtimeGain: На multi-socket серверах — до 2x. На desktop (один socket) — ноль.
  • compileTimeCost: Низкая
  • tradeoffs: Усложняет allocator, на desktop бесполезен.
  • existingUsage: Windows VirtualAlloc2/MapViewOfFileNuma2, но в desktop UI это почти никто не делает
  • rustFit: Не нужен. Fera — desktop Telegram client, один socket гарантирован. Скип.
  • idea: Производящий тред (UI) и потребляющий (render) жёстко пинятся на разные физические cores через SetThreadAffinityMask. Гарантирует, что их cache lines не мигрируют.
  • runtimeGain: На Windows даёт предсказуемость: меньше context switches, лучше locality. Измеримо на 144+ Hz дисплеях.
  • compileTimeCost: Низкая
  • tradeoffs: На гетерогенных (P/E cores Intel 12th+) требует знания топологии; на маленьких системах может навредить.
  • existingUsage: rayon, tokio (work-stealing, не strict pinning), rigtorp libraries (lock-free, cache-pinned)
  • rustFit: Просто. WinAPI через windows-rs, на старте приложения.
  • idea: Все долгоживущие state-объекты (signal graph, widget state) живут в host. DLL экспортирует чистые функции вида fn render(state: &State, frame: &mut Frame). При hot-swap state не трогается, только code меняется.
  • runtimeGain: Hot-swap становится тривиальным: выгрузил code, загрузил code, state на месте.
  • compileTimeCost: Высокая (сериализация state)
  • tradeoffs: Требует дисциплины: никакого state в static переменных DLL (это ломает hot-swap); вся долгоживущая память — host-owned.
  • existingUsage: Hotline использует именно это; Unreal live coding аналогично
  • rustFit: Идеально с compiled signal graph: граф — это данные, код над ним — swappable. Согласуется с Elm-outside архитектурой.

Feature detection вместо version match

Section titled “Feature detection вместо version match”
  • idea: Host при старте вызывает u32 get_abi_version() из DLL. Если major версия отличается — отказ. Если minor — работаем, но host знает, что часть features недоступна. Семантическое версионирование на уровне ABI.
  • runtimeGain: Позволяет постепенный rollout: новый host со старой DLL работает в degraded режиме.
  • compileTimeCost: Низкая
  • tradeoffs: Требует дисциплины в bump версий; не ловит layout-несовместимость (для этого — fingerprint).
  • existingUsage: Obsidian, Unity native plugins, FlatBuffers versioning, Protobuf field numbers
  • rustFit: Тривиально. Один extern fn + константы.

Notes: Браузер вёл себя нестабильно (редиректы на другие страницы, нестандартная среда), поэтому опирался на training data по crates.io/GitHub, который по этой теме (stable ABI, hot-swap, lock-free ring buffers, EBR/hazard pointers) достаточно полон и не требует свежих новостей.

Ключевые выводы для Chroix:

  1. nylon-ring как fork baseline — ОК. Он уже flat repr(C), без vtables, host-сайд заполняет структуру вызовов. Его слабые места: (а) версионируется руками, (б) нет чёткой дисциплины layout при hot-swap, (в) нет shared-memory режима. Всё это дорабатывается локально — менять baseline не нужно.

  2. Для hot-swap Chroix нужны три вещи, которых нет из коробки:

    • Versioned struct envelope ({ magic: u64, size: u32, abi_ver: u16, … } как в Vulkan sType/pNext) — все top-level структуры начинаются с этого, дальше append-only поля. Это единственный работающий паттерн, который используют Vulkan, Win32, COM, Obsidian, Unreal — все.
    • Double-buffer + quiesce barrier вместо полноценного EBR/HP. UI-фрейм имеет естественную точку quiescence — конец кадра. Хост ждёт конца текущего кадра, выгружает старую DLL. Это проще и предсказуемее любого lock-free реклеймера.
    • Layout fingerprint через build.rs, который хеширует layout всех repr(C) структур (имена, offsets, sizes) и встраивает в DLL как exported symbol. Host при LoadLibrary сверяет fingerprint — при расхождении грузит старую или откатывается. Убирает целый класс UB.
  3. Что НЕ брать:

    • abi_stable — тяжёлый, тащит свою runtime, vtables внутри, конфликтует с уже решённым “link-time dispatch без vtables”. Использовался в sauron (но тот на него забил) и ряде старых plugin-систем. Для Chroix overkill.
    • stabby — активно развивается (nightly-only repr), но подразумевает stable layout для Vec/String/Option через FFI. Хорош, если DLL-и компилируются разными версиями rustc. В Chroix обе стороны собираются одним toolchain — не нужен, и его generic-ABI это огромная compile-time цена.
    • Полноценный hazard pointers (как в folly) — для UI hot-swap overhead не оправдан, кадровый barrier дешевле.
  4. Что брать сверху:

    • bipbuffer или shmem-ipc ring buffers если host/DLL работают на разных тредах.
    • cache-padded / crossbeam-utils::CachePadded — обязательно для всех hot counters/head/tail.
    • cbindgen для генерации C-header’ов из repr(C) типов — чтобы можно было вручную сверять layout между версиями в CI.
    • Паттерн “factory через extern fn, возвращающую fat struct of fn pointers” — как в dll-syringe/plugin системах, но без vtable: обычный struct { draw: fn(…), layout: fn(…) } — это и есть ваш link-time dispatch.
  5. NUMA — на Windows desktop UI можно смело игнорировать. Один сокет, мало ядер, latency разницы нет. Не усложняйте.

  6. Cache-line awareness — да, критично: producer/consumer heads ring buffer на разных cache lines, arena region headers padded, hot signal state не шарит line со статикой. Это даёт измеримый выигрыш на 60fps петле.

  7. Static pointers через #[no_mangle] pub static — работает, но Windows DLL имеет свои .data/.bss секции и они не shared между host/DLL — то есть “static pointer в host” и “тот же static в DLL” это разные адреса. Использовать только как one-way: host экспортирует pointer, DLL его читает через GetProcAddress. Обратное — нет.

Смотри также: libloading (база для LoadLibrary), shared_memory crate (windows/file_mapping backend), memmap2 если пойдёте в mmap-backed ring.

Multi-rate render lanes с per-lane deadline

Section titled “Multi-rate render lanes с per-lane deadline”
  • idea: Разделить контент на частотные полосы: stickers/video = 60-75fps, chat list = 45fps при активном скролле и 24fps в покое, chrome/typing indicator = 24fps. Каждый lane имеет свой deadline, свой dirty bit, и свой render pass. Frame scheduler комбинирует lanes в один present, пропуская lanes, чей dirty не взведён или чей интервал ещё не наступил. При пропуске — берём последний кэшированный layer этой полосы.
  • runtimeGain: На 24fps-контенте (чтение чата) CPU/GPU load падает в 2.5-3× против 60fps everywhere. На комбинированной сцене (стикеры + текст) стикеры идут на 75fps, текст на 45 — экономия 40% кадров текста. Ключевое: при отсутствии изменений вообще — 0 кадров (idle = 0 GPU)
  • compileTimeCost: Низкий: это runtime scheduler + приоритеты; единственный compile-time кусок — генерация lane-assignment из signal graph (макросы уже есть для графа)
  • tradeoffs: Сложность композитора: нужно уметь composite N слоёв разной свежести в один кадр (Vello layers или wgpu texture blits). Риск jitter при пересечении lane boundaries (стикер 75fps поверх текста 24fps может визуально «плыть» при синхронной анимации). Требует disciplined invalidation — пропущенный dirty = застрявший пиксель.
  • existingUsage: Chromium Compositor Scheduler (разные задачи на BeginImplFrame vs BeginMainFrame), Unity/Unreal render thread lanes, Bevy run criteria / FixedUpdate, Discord/Slack (Electron) — throttling фоновых каналов
  • rustFit: Отлично: lane = enum + RingBuf последних layers; scheduler — маленький actor на host-стороне (за пределами fload DLL boundary, что важно: timing не пересекает dylib); dirty flags от compiled signal graph приходят как compile-time известные маски. Не требует repr(C).

Retained display list + damage tracking на уровне узлов

Section titled “Retained display list + damage tracking на уровне узлов”
  • idea: Display list снабжается stable IDs (из compiled signal graph — каждый узел знает свой stable key). На каждом кадре строим новый DL, сравниваем с предыдущим по IDs, получаем список изменённых AABB. Рендерим только damaged regions в offscreen, потом blit damaged rects в swapchain через dirty-rect present (DXGI_PRESENT_PARAMETERS на Windows, VK_EXT_swapchain_maintenance1, или Metal partial present).
  • runtimeGain: Типовой чат-скролл трогает 10-30% экрана → 3-5× меньше fragment work. Idle cursor blink = один 20×20px rect → почти нулевой кадр. Особенно мощно на ноутбучных iGPU где fragment bandwidth — узкое место.
  • compileTimeCost: Низкий: damage tracking это логика на display-list diffing; можно генерировать статически известные «damage classes» из сигнал-графа
  • tradeoffs: Нужен persistent swapchain/backing store (flip model). На Linux Wayland damage — нативен, на Windows требует DXGI 1.4+, на macOS — IOSurface partial update. Vello как immediate-mode renderer не дружит с partial redraw из коробки: нужно либо перерендерить damaged region в отдельный texture и composite, либо форкать/оборачивать Vello scene builder. Сложность clipping: damage rect пересекающий transformed/clipped node требует conservative bbox.
  • existingUsage: WebRender (Picture Cache + tile invalidation), Chromium CC (DamageTracker), Flutter Impeller (dirty region management), macOS Core Animation (setNeedsDisplayInRect), Windows DWM (dirty rects в flip model)
  • rustFit: Идеально для Chroix: display list уже contract (plain data), diffing — чистая Rust-функция, testable headless. Stable IDs приходят из compiled signal graph — бесплатно. Dirty-rect present на wgpu требует либо raw-window-handle + platform code (Windows DXGI), либо wgpu-hal extension — но host-side, не в DLL.

Текстурный атлас для UI-растров (стикеры, эмодзи, аватары)

Section titled “Текстурный атлас для UI-растров (стикеры, эмодзи, аватары)”
  • idea: Все мелкие raster (иконки, эмодзи, аватары 64×64, стикерные кадры после CPU-raster) упаковываются в 2-4 больших атласа 2048². Кадр стикера после tlottie/velato CPU-raster пишется прямо в атлас-rect через texture write_region. Vello сэмплирует из атласа по UV. Атлас persistent между кадрами — перезаписываются только слоты изменившихся стикеров.
  • runtimeGain: Один texture bind на весь UI вместо десятков → draw call collapse. Запись 512² кадра в атлас = 1 MiB write vs 1 MiB upload новой текстуры, но без аллокации. Стикерный loop-resident атлас убирает per-frame upload вообще.
  • compileTimeCost: Средний: атлас-пакер на этапе build-time для статических ассетов (gen atlas в build.rs), runtime packer для динамики
  • tradeoffs: Bleeding между соседними rect при linear filtering — нужен 1-2px padding. Fragmentation при динамических размерах. Атлас overflow → нужна политика eviction (LRU по стикерам). Vello 0.10 работает с images не идеально — может потребоваться кастомный image resolver.
  • existingUsage: Google Skia (GrAtlasManager), Flutter engine, Chrome UI (градиенты/иконки в атласах), игровые UI (Dear ImGui single-font-atlas), Telegram Desktop (стикерные атласы), tlottie→bitmap→atlas паттерн в TDLib
  • rustFit: Идеально: etagere / guillotiere crates для rectangle packing, wgpu queue.write_texture для update, Vello принимает images с UV. Может жить за fload boundary как часть renderer dylib (attlas management — внутреннее дело engine).
  • idea: Vello Scene собирается за один проход: все push_layer с одинаковым blend/clip идут подряд, glyphs батчатся в один glyph run, images ссылаются на атлас (см. атлас технику) — тогда один сэмплер. Критично: сортировать display list перед lowering в Scene так, чтобы state changes (shader/texture/blend) были монотонны. Reuse Scene через reset() и builder pattern (уже в fera-ui-engine-vello).
  • runtimeGain: Vello уже эффективен, но state churn убивает: один кадр с 50 отдельными image textures может дать 50 texture binds vs 1 при атласе. Reusable Scene убирает аллокации на кадр (~50-100µs экономии на большом DL).
  • compileTimeCost: Низкий-средний: паттерны батчинга в коде, не в типах
  • tradeoffs: Сортировка DL перед rendering нарушает painter’s order если делать наивно — нужно stable sort по (z, blend, texture) и сохранять back-to-front. Vello internals уже батчат path encoding, выигрыш в основном на image/text state changes. Over-engineering риск: профилировать до оптимизации.
  • existingUsage: Vello (coarse path rasterizer сам батчит по encoding), WebRender (batching по shader/texture в same render pass), Flutter Impeller (entity pass batching), SDF text (один draw на весь текст), Bevy (render phase batching)
  • rustFit: Отлично: vello::Scene имеет reset(), kurbo paths дёшево клонируются через Arc. Lowering проходит один раз за кадр — совместимо с 24/45/60-75 lanes.

Async text shaping pre-pass на worker pool

Section titled “Async text shaping pre-pass на worker pool”
  • idea: Parley shaping для видимых+adjacent сообщений запускается в rayon pool параллельно с main thread Vello Scene build предыдущего кадра. Результат — готовый parley::Layout, кэшированный по (text hash, width bucket, font version). При следующем frame layout уже готов. Для message list: shape-ahead на 1 экран вверх/вниз от viewport.
  • runtimeGain: Text shaping — 20-40% кадрового времени в текстовом UI. Вынос в async даёт почти zero-cost текст на main thread (только glyph upload). Для длинного чата: shaping 50 сообщений параллельно на 4 cores vs последовательно = 3-4× ускорение scroll-into-view.
  • compileTimeCost: Низкий: это scheduling
  • tradeoffs: Speculative shaping тратит CPU на контент, который может не показаться (overscroll). Font fallback resolution усложняет кэш-key. Нужна отмена in-flight shaping при быстром скролле (epoch/generation counters).
  • existingUsage: Chromium (font shaping в worker threads), Servo (parallel layout), Flutter (text layout на UI thread но async raster), Cosmic-text (shaping cache), Parley (LayoutContext reuse)
  • rustFit: Отлично: Parley Layout Send (после завершения shaping), кэш — обычный RwLock. Совместимо с Elm-outside: shaping это не view-функция, а подготовка ресурса. fload boundary не мешает — shaping может жить в отдельном модуле.

Frame caching + re-present без re-render (layer cache)

Section titled “Frame caching + re-present без re-render (layer cache)”
  • idea: Статичные полосы (chrome, sidebar, список чатов в покое) рендерятся в собственные texture layers один раз, далее каждый кадр — только composite этих textures + re-render только активных lanes (стикеры). Composite — дешёвый fullscreen textured quad. При dirty в статичной полосе — re-render только её texture, не всего кадра.
  • runtimeGain: Idle чат с мигающим курсором: 1% GPU против full re-render. Scroll на 45fps: message list layer кэширован, рендерится только viewport delta. Battery: GPU sleep между композитами.
  • compileTimeCost: Низкий
  • tradeoffs: VRAM pressure: 4K fullscreen RGBA layer = 33 MiB; 3-4 layers = 100+ MiB. Resize окна инвалидирует все layers. Transform/animation на layer требует re-render или GPU-side transform (усложняет композитор).
  • existingUsage: WebRender (tile cache: статичные страницы = re-composite cached tiles), Flutter RasterCache, macOS Core Animation (layer backing stores), game UI (menu screens), Android SurfaceFlinger (переиспользование буферов)
  • rustFit: Отлично: wgpu Texture для каждого layer, composite pass — минимальный render pass. Vello рендерит в texture через render_to_texture. Layer tree совместим с display list contract.

Power management: background throttling + lane pause

Section titled “Power management: background throttling + lane pause”
  • idea: Visibility-driven lane gating: unfocused window → все lanes кроме critical (badge, звук) на 0 fps. Minimized → render thread parked. Per-signal energy class: compiled signal graph помечает каждый сигнал как Critical/Normal/Cosmetic; в power-save Cosmetic lanes принудительно на 12fps или off. Стикерные циклы останавливаются если sticker не в viewport (IntersectionObserver-style).
  • runtimeGain: Background window: 0% GPU vs passive 3-8%. Laptop battery: 30-60 мин экранного времени. Cosmetic-off mode: -20-30% GPU на слабом железе.
  • compileTimeCost: Низкий
  • tradeoffs: Resume latency: возврат из deep idle должен быть <100ms чтобы не чувствовался. Неправильная классификация сигнала (Cosmetic на самом деле важен) = UI bugs. Per-platform различия в suspend semantics.
  • existingUsage: Chromium (background tab throttling до 1% CPU), Android (Choreographer frame callback gating), iOS (CADisplayLink pause в background), Discord (background suppression), Telegram Desktop (останавливает sticker loops при unfocus)
  • rustFit: Идеально: winit события Focused/Suspended → host scheduler → lane enable mask. Cosmetic классы — compile-time из signal graph (макросы могут генерировать энергетический класс из типа сигнала).

Present mode policy: Mailbox vs FIFO + adaptive switching

Section titled “Present mode policy: Mailbox vs FIFO + adaptive switching”
  • idea: Mailbox (aka triple buffering) как дефолт для активного скролла/анимаций: swapchain держит 3 буфера, present никогда не блокирует, старый кадр молча дропается. Fifo (vsync-strict) для статичного контента — меньше power. Adaptive: при frame time <80% budget → Mailbox, иначе → Fifo для плавности. FifoRelaxed на Windows для tearing-tolerant сцен.
  • runtimeGain: Mailbox при скролле: input-to-photon latency падает на 8-16ms (1 кадр) против Fifo. Fifo при idle: GPU downclock раньше → power. Правильный режим убирает очередь кадров (queue buildup = lag).
  • compileTimeCost: Низкий
  • tradeoffs: Mailbox тратит GPU на кадры, которые не покажутся (drpoed). На некоторых Android драйверах Mailbox энергозатратнее. Adaptive logic добавляет hysteresis complexity. FifoRelaxed не везде поддерживается.
  • existingUsage: wgpu::PresentMode::{Fifo, Mailbox, Immediate, FifoRelaxed}, DXGI flip model, Android SurfaceFlinger, X11 Present extension, все game engines
  • rustFit: Идеально: wgpu PresentMode — один enum, host-side выбор. Динамическое переключение — reconfigure surface. Не пересекает fload boundary.

Triple buffering и временной pipeline

Section titled “Triple buffering и временной pipeline”
  • idea: 3 swapchain images при 60-75fps, 2 при 24/45 (экономия VRAM). Кадр N рендерится в то время, как N-1 present, N-2 в очереди. С careful synchronization: acquire next image → если не готова за 2ms — render в кэшированный frame и skip (не блокируем main thread).
  • runtimeGain: CPU не ждёт GPU: submit сразу после render, не ждём present. При frame time spikes один плохой кадр не каскадится. 24fps lane с 2 буферами: -33 MiB VRAM.
  • compileTimeCost: Низкий
  • tradeoffs: Третий буфер = +33 MiB на 4K. Acquire timeout fallback требует повторного использования предыдущего кадра (нужен retained framebuffer). На Windows DXGI flip model уже почти это делает под капотом.
  • existingUsage: wgpu Surface configuration, DXGI_SWAP_EFFECT_FLIP_DISCARD, Vulkan mailbox present mode, Metal drawable pool
  • rustFit: Отлично: wgpu управляет image count через desired_maximum_frame_latency. acquire_timeout паттерн на host side.
  • idea: Vello уже использует compute для path flattening — расширить: glyph atlas upload, image decode (JPEG→texture через compute), damage-region clear, atlas compaction — всё на compute queue параллельно graphics queue. wgpu позволяет submit command buffers на разных queues (если device exposes).
  • runtimeGain: Image decode и upload не блокируют render pass. На iGPU с shared queues — мал; на дискретных с отдельными compute queues — 10-20% frame time. Главное: убирает long render pass stalls.
  • compileTimeCost: Низкий
  • tradeoffs: wgpu multi-queue support ограничен — на практике всё равно одна queue семья; выигрыш скорее от better packing command buffers. Сложность синхронизации (barriers между compute и graphics). На слабом железе overhead может превысить gain.
  • existingUsage: wgpu compute pipelines, Vello (path coarse raster уже compute), Unreal Nanite, WebGPU async compute proposals
  • rustFit: Хорошо: wgpu ComputePass API прямой, Vello already compute-heavy. Но multi-queue в wgpu пока ограничен (одна queue семья на device обычно).

Headless golden-frame backend параллельно Vello

Section titled “Headless golden-frame backend параллельно Vello”
  • idea: chroix-headless (уже запланирован) используется не только для тестов, но как production fallback: при слабом GPU/remote desktop/battery saver — рендер в CPU buffer через vello_cpu, композит через sw blit. Тот же display list, тот же damage tracking, другой backend.
  • runtimeGain: Не runtime gain, а testability: каждая оптимизация (damage, lanes, caching) проверяется golden frames без GPU флакиности. CI ловит регрессии damage tracking пиксель-в-пиксель. Также coverage для vello_cpu fallback path.
  • compileTimeCost: Низкий
  • tradeoffs: Дублирование тестовых сценариев если API расходится. vello_cpu performance не отражает GPU reality — нужны оба бенчмарка.
  • existingUsage: Vello (render_to_image API), Flutter golden tests, WebRender wrench reftests, Bevy headless rendering
  • rustFit: Идеально: vello_cpu 0.2 уже собран и работает (по fera-ui-stack). Тот же Engine trait из ui-contract.

Tile-based rendering для message list (WebRender-style)

Section titled “Tile-based rendering для message list (WebRender-style)”
  • idea: Message list — это вертикальный strip из tiles 256×256. Viewport + 1 tile margin рендерятся в кэш; скролл = сдвиг UV + re-render только вновь вошедших tiles. Каждый tile имеет dirty flag от display list diff. Рендер tile в offscreen atlas-like texture.
  • runtimeGain: Scroll: 90% экрана — cached tiles, re-render 1-2 новых. Memory: 4K list = не 33 MiB fullscreen, а 8-10 tiles = 5-8 MiB. Damage локализован: изменение одного сообщения = 1 tile, не full list.
  • compileTimeCost: Низкий
  • tradeoffs: Tile boundary artifacts при transforms/clip. Overscroll/rapidl scroll требует быстрого re-render — нужен приоритет видимых tiles. Sticky headers/avatars усложняют tile mapping.
  • existingUsage: Chromium GPU raster + tiles, WebRender PictureCacheTile, Flutter Impeller, Картографические движки (map tiles)
  • rustFit: Хорошо: tile indexing — plain math, Vello рендерит в texture, LRU eviction — стандартный. Совместимо с display list (tile = subtree).

VRAM-resident loop frames для стикеров

Section titled “VRAM-resident loop frames для стикеров”
  • idea: Стикерные 90 кадров в VRAM как atlas-slot на весь цикл (см. атлас технику): tlottie/velato raster каждого кадра однажды → все 90 frames в атласе → стикер играет бесконечно без CPU. Для 50 стикеров = 50×22.5MiB@256² = 1.1 GiB VRAM — квотируется (топ-N видимых).
  • runtimeGain: Steady-state stickers: 0% CPU после загрузки. Compare с tlottie live decode: 50 стикеров × 30fps × 1 MiB decode = 1.5 GB/s CPU → 0.
  • compileTimeCost: Средний: retention policy — дополнительная логика
  • tradeoffs: 1.1 GiB VRAM на стресс-сцене — не влезает в слабые iGPU (512 MiB). 256² экономит 4× но качество страдает на Retina. Eviction + re-raster при cache miss = jitter. Нужна политика: loop-resident только для on-screen + recently-seen.
  • existingUsage: Game engines (sticker/animation loops), Telegram Desktop (sticker loop), Discord (emoji loops)
  • rustFit: Хорошо: интегрируется с атласом. Velato (Vello+Lottie) может рендерить напрямую в GPU texture. Квота eviction — host-side policy.

Display list как glyph-run + image-ref format

Section titled “Display list как glyph-run + image-ref format”
  • idea: Parley даёт shaped glyphs → они хранятся как (glyph_id, x, y) runs, а не pixels. Glyph atlas (single texture) обновляется инкрементально: новый glyph = rasterize один раз, добавить в atlas. Кадр рендерит text как instanced quads из atlas. Damage на тексте = перегенерация runs, но glyphs уже в atlas.
  • runtimeGain: Text rendering без shaping в steady state: один glyph уже в atlas = free. Scroll текста = re-use glyph runs, только transform. Типичный чат: 95% glyphs уже в atlas после первого экрана.
  • compileTimeCost: Низкий: формат данных, не код
  • tradeoffs: Glyph atlas overflow на CJK (нужны большие atlas или eviction). Sub-pixel positioning усложняет cache (разные subpixel offsets = разные glyphs). Font fallback → cache invalidation.
  • existingUsage: GPUI (text runs как positioned glyph IDs), WebRender (glyph cache), Flutter, Photoshop
  • rustFit: Идеально: Parley даёт glyph positions, swash/ cosmic-text rasterizes glyphs в atlas. Vello имеет glyph support. Совместимо с display list contract.

Notes: Привязка к уже решённому в Chroix/Fera: (1) compiled signal graph + Elm-outside означает, что damage information приходит из графа бесплатно — не нужен runtime read-tracking ради dirty-rect, сигналы знают что поменялось; используйте это как вход для damage tracking, не повторяйте Salsa. (2) Display list как backend contract = идеальное место для retained scene: display list уже сериализуемая структура, её же можно диффить. (3) Vello+headless пара: damage/dirty-rect логика тестируется на headless без GPU. (4) fload DLL boundary: renderer в DLL, поэтому frame pacing/scheduling — на host-стороне (winit event loop), а не внутри dylib; dylib отдаёт готовый Scene/текстуры, host решает когда present. (5) Frame budgets 24/45/60-75 из Fera QA напрямую мапятся на multi-rate lanes. (6) Уже существующий код: fera-ui-engine-vello делает reusable scene reset per frame — это первая ступень к retained scene, но сейчас full-rebuild. Ключевые проекты для кражи идей: WebRender (tile cache, picture cache), Chromium CC (damage, scheduler), Flutter (raster cache, partial repaint в Impeller), Cosmic-text/Parley для shaping cache, Bevy (multi-rate), wgpu::PresentMode, DXGI_SWAP_EFFECT_FLIP_DISCARD + dirty rects, Velato для Lottie→Vello, tlottie как CPU-raster источник битмапов в атлас.

Per-frame bump arena (bumpalo, phases reset)

Section titled “Per-frame bump arena (bumpalo, phases reset)”
  • idea: Одна arena на кадр (или на фазу: build / layout / paint), в неё скидываются все временные ноды, display-list-команды, промежуточные строки; в конце кадра один reset() вместо N free. В Chroix — три арены под три бюджета (24/45/60–75 fps), живут на стороне host за fload-границей.
  • runtimeGain: Убирает ~весь alloc/free трафик hot path; идеальная локальность (все данные кадра — один contiguous chunk, prefetch-friendly); O(1) деаллокация всего кадра.
  • compileTimeCost: Низкая — bumpalo маленький, без макросов-монстров.
  • tradeoffs: Нет per-object free, нет Drop для содержимого (только Copy/plain data или ручной boxed::Box); при переполнении chunk растёт — пик памяти = худший кадр; нужна дисциплина «не тащить &’_ за пределы кадра».
  • existingUsage: bumpalo; bumpalo-herd для per-thread пулов; Vello (vello::Scene + Encoding reset per frame); Dioxus templates.
  • rustFit: Отлично. Bump !Sync, поэтому по одной на поток или bumpalo-herd; лайфтаймы естественно выражают «не переживает кадр». Важно: арена живёт в host, плагину отдаётся &mut [u8]/SceneBuilder — host-leased buffers, как уже принято в nylon-ring/fload.

Flat node pool с generational indices (slotmap)

Section titled “Flat node pool с generational indices (slotmap)”
  • idea: UI-дерево = Vec<Node> + u32 индексы (parent/first_child/next_sibling) вместо Box/Rc. Для списков с виртуализацией (чат Telegram) — generational indices (slotmap) чтобы безопасно переиспользовать слоты без ABA.
  • runtimeGain: Нет pointer chasing и allocator pressure; обход дерева — последовательный проход по памяти; стабильные NodeId для display-list contract и для headless-тестов.
  • compileTimeCost: Низкая; slotmap — маленький крейт.
  • tradeoffs: Две косвенности на lookup (index → slot); удаление не мгновенно освобождает память (freelist); generational check — лишняя ветка на каждый доступ.
  • existingUsage: slotmap; slab; Taffy (TaffyTree поверх slotmap-like storage); AccessKit TreeUpdate; egui Memory.
  • rustFit: Отлично; NodeId = Copy-тип, безопасно передаётся через fload как plain data (не repr(C) структура, а u64 key).

AoS для дерева, SoA для горячих проходов (hybrid)

Section titled “AoS для дерева, SoA для горячих проходов (hybrid)”
  • idea: Само дерево — AoS (Node ~64–128 B: style-id, layout-cache, flags, child range). Для горячих SIMD-проходов (measure, cull, paint-order) на кадр строится SoA-scratch (отдельные колонки x/y/w/h/flags) в той же bump-арене. Не хранить SoA постоянно — генерить per frame.
  • runtimeGain: AoS даёт нормальный random access при редактировании; SoA даёт векторизацию и плотный streaming на layout/paint, где обходятся тысячи нод.
  • compileTimeCost: Средняя — гибрид требует двух представлений + код перегона; const-generics helpers могут раздуть мономорфизацию.
  • tradeoffs: Двойная запись при изменении; код сложнее; на маленьких UI (<1k нод) SoA не окупается — порог включения по размеру дерева.
  • existingUsage: Bevy ECS (SoA archetypes + sparse sets); Servo/Stylo; morphorm.
  • rustFit: Хорошо; SoA-scratch в bump-арене — идиоматично; SoA-структуры держать внутри engine, не выставлять наружу.

Interning строк и стилей (Symbol = u32)

Section titled “Interning строк и стилей (Symbol = u32)”
  • idea: Все строки (class names, style keys, accessibility labels, text runs) и style-блоки интернируются один раз при загрузке темы/компиляции view, дальше по коду ходит u32 Symbol. Сравнение стилей = сравнение u32; hash map lookups исчезают из per-frame.
  • runtimeGain: O(1) сравнение/хеширование идентификаторов; дедупликация — одинаковые стили хранятся один раз; computed-style cache становится Vec<ComputedStyle> индексируемый Symbol’ом.
  • compileTimeCost: Низкая; lasso/string-interner маленькие, тяжёлый макро-код не нужен.
  • tradeoffs: Интернер растёт монотонно (или нужен GC по epoch); дополнительный lookup на горячем пути если текст приходит извне каждый кадр — тогда интернировать только ключи стилей, а не контент.
  • existingUsage: lasso; string-interner; hashbrown raw-entry; Salsa interner pattern; egui Id; GPUI ElementId; Parley (font cache).
  • rustFit: Отлично; Symbol — Copy + Send + Sync, свободно пересекает fload-границу. Не дублировать то, что уже интернит Parley/Skia (glyph runs, fonts).

Zero-allocation hot path (smallvec/arrayvec + reuse scratch)

Section titled “Zero-allocation hot path (smallvec/arrayvec + reuse scratch)”
  • idea: Все временные Vec/HashMap/String в build/layout/paint — либо bumpalo::collections, либо SmallVec на стеке (children до N, style overrides до M), либо переиспользуемые scratch-буферы в Engine (double-buffered). Цель: 0 аллокаций на steady-state кадр.
  • runtimeGain: Убирает jitter от аллокатора — критично для 24 fps бюджета; предсказуемый worst-case frame time.
  • compileTimeCost: Низкая; smallvec/arrayvec/tinystr — tiny crates.
  • tradeoffs: Требует дисциплины и аудита (любой .to_string()/format! в hot path — регрессия); smallvec при переполнении молча уходит в heap — нужны debug-asserts на capacity.
  • existingUsage: smallvec; arrayvec; tinystr; heapless; Vello (smallvec для path segments); Parley.
  • rustFit: Отлично; естественно сочетается с compiled signal graph — раз граф статический, размеры scratch можно оценить на этапе компиляции view.

Copy-on-write / слайс-строки через индексы (Rope-lite)

Section titled “Copy-on-write / слайс-строки через индексы (Rope-lite)”
  • idea: Строки не String, а StrSpan { buf: ArenaPtr/StaticPtr, start: u32, len: u32 } — substring, concat, trim — только манипуляции индексами без копирования байт. Для статического текста (лейблы из шаблонов) — &'static str напрямую; для динамического — один большой text-blob на кадр в арене.
  • runtimeGain: Сборка текста кадра без memcpy; trim/slice — O(1); меньше churn в allocator.
  • compileTimeCost: Низкая; copypasta/extent — обычные структуры.
  • tradeoffs: Указатель на арену живёт не дольше кадра — нельзя сохранить StrSpan между кадрами; сложнее отлаживать; concat через индексы требует отдельного build pass.
  • rustFit: Хорошо, но осторожно: полноценный COW-subtree для UI — ловушка (atomic Rc traffic + промахи кэша). Применять только к строкам и к доказуемо-иммутабельным static template fragments, которые compiled graph может выделить как const.

Memory-mapped theme/style blob (zero-deserialize)

Section titled “Memory-mapped theme/style blob (zero-deserialize)”
  • idea: Тема/стили компилируются в flat binary blob (rkyv или capnp-like), mmap’ится read-only; чтение стиля = dereference указателя в mmap, без парсинга и аллокаций на старте. Холодные страницы подгружаются ОС по требованию.
  • runtimeGain: Мгновенный запуск UI (нет парсинга темы); нулевой RSS на неиспользуемые темы; share между процессами/окнами.
  • compileTimeCost: Средняя — rkyv/capnp имеют свои proc-macro/схемы; но это один раз для формата темы.
  • tradeoffs: Нужен offline-компилятор темы; версионирование формата; unsafe при чтении (rkyv archive access); правка темы = пересборка blob, не hot-edit без доп. слоя.
  • existingUsage: rkyv (zero-copy deserialize); capnp; memmap2; паттерн из game assets.
  • rustFit: Умеренно. Конфликт с принципом fload «no repr(C)» решается тем, что blob — opaque bytes на границе, а типизированный доступ остаётся внутри engine. Требует отдельного compile-step для темы.

Reuse pools + double-buffered layout (prev/cur)

Section titled “Reuse pools + double-buffered layout (prev/cur)”
  • idea: Две bump-арены: в кадр N пишем в A, читаем из B (предыдущий layout для dirty-check и hit-test); в конце кадра swap. Удалённая нода не free’ится — её слот уходит во freelist и переиспользуется на следующем кадре.
  • runtimeGain: Dirty-checking против предыдущего кадра — сравнение двух плоских Vec, а не обход с refcount; никаких free-list-локов; стабильные адреса внутри кадра.
  • compileTimeCost: Низкая.
  • tradeoffs: Пик памяти = 2× дерева; dirty-check всё ещё O(tree) (хоть и дешёвый); нужен явный протокол «кто владеет cur/prev».
  • existingUsage: Dioxus (double-buffered templates); Vello encoding reuse; Parley Layout cache.
  • rustFit: Отлично для compiled signal graph: раз граф статический, двойной буфер выделяется один раз под известный max-размер. Ложится на 24/45/60–75 fps бюджеты — разные пулы на разные частоты.

Custom global allocator + segregated heaps

Section titled “Custom global allocator + segregated heaps”
  • idea: Поверх bump-арен — кастомный global allocator (mimalloc с настройкой arena per thread) или отдельный slab для «долгоживущих» объектов (шрифты, glyph cache, decoded images), чтобы они не фрагментировали общий heap и не смешивались с per-frame churn.
  • runtimeGain: Меньше contention и фрагментации; предсказуемый p99 frame time; long-lived данные не мешают hot path.
  • compileTimeCost: Низкая.
  • tradeoffs: Один allocator на процесс — конфликтует с hot-swappable модулями, если они тоже хотят свой; нужно дисциплинировать плагины не тащить свой allocator через DLL-границу.
  • existingUsage: mimalloc; jemalloc; паттерн из OPTIM-IDEAS-3-research (plugins slab); Blender/Chrome partition alloc.
  • rustFit: Хорошо; #[global_allocator] — стабильный механизм; но только один на процесс — если fload-плагины тоже линкуют свой, конфликт. Держать allocator в host, плагины аллоцируют через host-API.

Structural sharing для иммутабельных фрагментов (COW done right)

Section titled “Structural sharing для иммутабельных фрагментов (COW done right)”
  • idea: Для больших read-mostly поддеревьев (список чатов, статический sidebar) — persistent структура (im-rc::Vector / rpds), где «изменение» = новый root с structural sharing вместо копирования. Использовать только там, где subtree реально разделяется между кадрами и редко мутирует.
  • runtimeGain: Update большого списка без копирования; cheap snapshot для undo/diff.
  • compileTimeCost: Низкая.
  • tradeoffs: Rc/Arc трафик и cache-miss на pointer-chase — ровно то, от чего уходят flat Vec. Не применять в hot path paint/layout; применять только на границе данных (chat list model), не на границе виджетов.
  • existingUsage: im-rc; rpds; Jetpack Compose (persistent snapshots); Elm.
  • rustFit: Средне. В Chroix с compiled signal graph большая часть дерева статична — persistent слой нужен только на границе «реактивный список». Сочетать с interning — ключи фрагментов как Symbol.

Notes: Territory: memory layout & arena allocation for Chroix. Verified crates via crates.io (Playwright, 2026-10-03): bumpalo v3.20.3 current, features collections/boxed/allocator-api2 (stable shim over nightly Allocator), bumpalo-herd for multi-thread pools. Real-engine precedents cross-checked from first-hand knowledge + prior verified Fera-UI-stack notes (memory file fera-ui-stack.md): Vello (wgpu display list + per-frame vello::Scene bump), Parley (Layout reuse + font/skia-text interning), Taffy (taffy::tree::TaffyTree over slotmap), morphorm (flat ECS-ish storage), egui (Id interner + Memory retained pools), GPUI (App arena + ElementId), AccessKit (flat TreeUpdate nodes vec + NodeId), Bevy ECS (SoA archetypes + sparse sets), Clay (C, pure arena + integer ids), Yoga (leaf-node pooling), Stylo/Blink (style interning + computed-style cache), Dioxus (bumpalo per-template), Salsa/interner pattern, string-interner/lasso/hashbrown/smallvec/arrayvec/tinystr as concretes. Cross-cutting notes: (1) bump arena per FRAME (build/layout/paint) is the canonical UI pattern — allocate the whole frame, reset once, zero per-node free; Chroix’s compiled signal graph makes this easier because there is no runtime subscription bookkeeping that would need long-lived heap state. (2) Flat Vec+u32 indices beats Box pointers everywhere: predictable prefetch, no allocator pressure, stable indices for the display-list backend contract; generational indices (slotmap) only where nodes are actually removed/reused (chat list virtual scrolling), plain index for static UI. (3) AoS for node struct of ~64–128 B wins over SoA at UI scale (≤10⁴ nodes/frame); SoA pays off only on SIMD passes (layout measure, paint cull) — hybrid: AoS tree + SoA scratch columns built per frame from the arena. (4) Intern everything textual/style at the theme compile step (build-time when possible, else at theme load), not per-frame — Parley/Skia already do glyph-run interning, so Chroix inherits it for fonts; do not double-intern. (5) COW subtrees are mostly a trap in UI: Rc/Arc + memo break prefetch and add atomic traffic; prefer structural sharing only for static template fragments the compiler can prove immutable. (6) mmap for theme/style assets is worth it only if the theme format is a flat binary blob (capnp/rkyv-style zero-deserialize), which collides with fload’s no-repr(C) DLL boundary — feasible because theme data crosses the boundary as opaque bytes/vello scene, not as typed structs. (7) Hot-swap interaction: arenas must live on the host side of fload, never inside a swappable plugin’s address space, else a swap invalidates pointers — put bumpalo::Bump and node pools in the host, hand plugins &mut [u8]/SceneBuilder (host-leased buffers, matching nylon-ring pattern already noted in conversation.md:963). Compile-time cost across the board is low-to-moderate (arena/pool generics are cheap; heavy const-generics in SoA helpers are the main offender); none of this conflicts with LTO/PGO/BOLT/codegen-units=1.

  • idea: Каждый узел в compiled signal graph — это memoized query с явными входами. Изменился вход → инвалидируются только зависимые запросы. Red-цикл: Q(x) → Q(y) → Q(z) — при set(x) пересчитывается только цепочка. Salsa 2022+ даёт durability и interning, что критично для identity (один и тот же String ‘hello’ = один interned id, дифф сводится к сравнению u32).
  • runtimeGain: Стабильный 60fps при типичных правках: пересчёт O(изменённые узлы), а не O(весь UI). rust-analyzer держит ~5-15ms на re-analysis файла в 10k строк — это порядок, который нужен Chroix для hot path перерисовки. Также бесплатный snapshotting (Salsa::snapshot) для time-travel и отладки.
  • compileTimeCost: Низкий. Salsa — стабильный crate, публичный API макросами. Мономорфизация растёт линейно от числа запросов; одна signal-нода = одна query. Заметных скачков compile time нет.
  • tradeoffs: Salsa не магия — нужно правильно грануляризовать запросы (слишком крупные → сплошная инвалидация, слишком мелкие → overhead на tracking). Требует дисциплины ‘чистых’ функций — сайд-эффекты в query ломают memo. Громоздкий код при глубоких иерархиях.
  • existingUsage: salsa-rs/salsa (rust-analyzer — флагманский пользователь, обрабатывает терабайты логических изменений при живом редактировании), vercel/turbo (Turborepo вдохновлён), magmide, facet. Для UI: cranky (https://crankycoder.com), эскизы в Bevy community crates.
  • rustFit: Отлично. С Salsa граф зависимостей — compile-time типизирован, никакого runtime tracking. Естественно ложится на Elm-outside (state → query → view). Для fload boundary: interned ids передаются как u32, никаких Rust-типов через DLL. Salsa живёт на host-side, в DLL уходит уже готовый display list.

Structural sharing через im-rc / rpds

Section titled “Structural sharing через im-rc / rpds”
  • idea: Persistent B-tree vector (bitmapped vector trie, как в Clojure) для message list и любых больших списков. Ссылка на хвост O(1), вставка/удаление O(log32 n) — фактически константа. При изменении одного элемента shared-структура: unmodifiable prefix/suffix переиспользуется без копирования. На фронте diffing сводится к pointer-compare поддеревьев.
  • runtimeGain: Вставка 1 сообщения в 100k-список — O(log32 100k) ≈ 3-4 копии узла, а не realloc всего Vec. Diffing сообщения на экране: pointer compare поддеревьев — miss = только изменённая ветка. Кэш локальности хуже чем Vec, но полностью компенсируется отсутствием полного rebuild.
  • compileTimeCost: Низкий. im-rc — чистая структурная библиотека, типизированная мономорфизация на типах элементов. Добавляет ~10-20s к чистой сборке проекта.
  • tradeoffs: Хуже cache locality, чем Vec. Iteration медленнее ~1.5-2x. Rc/Arc overhead на каждый узел. Не подходит для горячих малых коллекций (<1000 элементов) — Vec + SlotMap выигрывает.
  • existingUsage: im-rc, rpds. В UI: Druid использовал im::Vector для больших списков до переписывания на Xilem; Yew имеет persistent list helper. Вне Rust: PersistentVector в Clojure, Immutable.js, React + Immer.
  • rustFit: Хорошо для message list, плохо как универсальный контейнер. im-rc даёт Rc-подсчёт ссылок — это single-thread, что идеально для UI-потока. Для fload: передаём иммутабельный snapshot целиком, либо Arcim::Vector для zero-copy. Не смешивать с Salsa-интернированием на одном и том же уровне — одна система identity.

SlotMap + generational IDs для UI-узлов

Section titled “SlotMap + generational IDs для UI-узлов”
  • idea: Все UI-узлы — это generational index (u32 id + u32 generation), хранящиеся в SlotMap. Удалил узел → generation инкрементируется, старые id становятся stale и безопасно отвергаются. Диффинг двух деревьев: hash по id — разный id = разный узел, одинаковый id + одинаковая generation = один и тот же узел, рекурсия по потомкам не нужна для удалённых веток.
  • runtimeGain: O(1) вставка/удаление, отсутствие дырок в памяти (dense storage), отличная cache locality при итерации. Диффинг поддерева = сравнение одного u64 (id+generation) — если совпал, рекурсия вниз не нужна. Это даёт быстрый bail-out на неизменённых subtree, что критично для больших списков.
  • compileTimeCost: Низкий. slotmap — маленькая библиотека (~2k LOC). Макрос new_key_type! генерирует типы; compile time почти не растёт.
  • tradeoffs: Generation — это не значение, которое можно сравнивать между независимыми снапшотами (разные снапшоты = разные SlotMap). Нужно явно версионировать snapshot’ы. Не решает проблему reordering внутри списка — для этого нужен отдельный механизм keyed diff.
  • existingUsage: slotmap crate. Bevy использует для Entity. Roblox Lua-адаптация. Servo для DOM nodes (аналогичная идея через arena).
  • rustFit: Отлично. SlotMap — чистый safe Rust без unsafe. Id передаётся через fload boundary как u64 — никаких указателей. Salsa interning и SlotMap ID можно слить в одну систему identity: query key = NodeId.

Keyed diffing для message list (Solid/Vue3-style)

Section titled “Keyed diffing для message list (Solid/Vue3-style)”
  • idea: Для списка сообщений keyed diff через ‘two-ended scan’ + LIS (Longest Increasing Subsequence) на хвосте. Алгоритм: скан с обоих концов пока совпадает, потом map key→index, визиты по map, для оставшихся — LIS даёт минимальное число moves. Это то, что использует Solid/Vue3.
  • runtimeGain: Перестановка 1000 элементов — O(n) в типичном случае (добавление в конец/начало) и O(n log n) в худшем. Практически: 100k-список с 1 вставкой — ~200ns на diff. Для message list в Telegram-клиенте: scrollback редко reorder’ится, типичный случай — prepend/append, что обрабатывается за O(k) где k = число изменённых.
  • compileTimeCost: Низкий-средний. Алгоритмическая сложность в коде, но без тяжёлых макросов. Своя реализация на ~200 строк — быстрее компилируется, чем similar.
  • tradeoffs: LIS — дополнительная сложность. При большом числе reordering патология может выродиться в O(n²). Нужен fallback: если reordering >30%, просто rebuild. Для очень коротких списков (<32) простой O(n²) brute force быстрее из-за константы.
  • existingUsage: Peterson-Miller-Myers в React (keyed reconciliation), Solid.js (mapArray), Lit-html repeat. В Rust: similar (diff для строк), imara-diff, keyed-list в Dioxus.
  • rustFit: Отлично. Алгоритм компактный, полностью safe, работает на &[K] с K: Hash+Eq+Copy. Совместим с persistent vector: diff работает поверх im::Vector через итератор. Для fload: отдаём host-стороне готовый список patch-операций (InsertAt/RemoveAt/MoveTo), а не сами ключи.

Dirty region tracking через bitset + damage rects

Section titled “Dirty region tracking через bitset + damage rects”
  • idea: На каждый layout/display-pass заводим bitset размером в число узлов. Layout-фаза помечает dirty бит на узле и всех предках. Display-фаза делает AND с битовой маской visible-region — пересчёт только dirty ∩ visible. Bitset компактный (8К узлов = 1KB) и сканируется SIMD (u64::trailing_zeros в цикле).
  • runtimeGain: Типичное обновление (новое сообщение в чате): 5-10 узлов dirty из 10000. Display list rebuild = O(dirty), а не O(total). Итоговое repaint = только damaged rects через Vello partial redraw (если wgpu поддерживает) или хотя бы scissor-rect. Это центральная оптимизация для 60fps на слабом железе.
  • compileTimeCost: Низкий. Чистая арифметика над &[u64] / bitvec. bitvec — средних размеров crate, даёт ~5-10s к сборке. Можно обойтись fixedbitset или ручным u64-сканированием.
  • tradeoffs: Требует явной дисциплины mark_dirty — забытая пометка = stale рендер. Альтернатива — версии/тики как в Bevy, но они дороже на read. Damage rect tracking может дробиться: 1000 мелких rect’ов хуже, чем один большой, нужен merge-порог.
  • existingUsage: Flutter RenderObject.markNeedsLayout + dirty list, Servo’s restyle damage, Bevy change detection (Ticks + bitmask per archetype), Emacs redisplay dirty flags.
  • rustFit: Отлично. Никакой динамики: bitset живёт в одном месте, инвалидирование через явный mark_dirty(id). Совместимо с compiled signal graph: выход каждой сигнальной ноды — dirty bits. Через fload передаётся готовый &[u64] или damage rect list.

Display list diffing на уровне Vello Scene

Section titled “Display list diffing на уровне Vello Scene”
  • idea: Display list — это flat Vec. Дифф двух списков — это sequence diff (как строки). Добавляем stable id к каждой команде (hash от NodeId + command slot), затем Myers O(ND) diff. Результат: reuse-префикс, insert/delete/move в середине, reuse-суффикс. Vello Scene строится инкрементально из стабильных сегментов.
  • runtimeGain: Скролл на 1px неизменного содержимого: 95% команд переиспользуются (только transform на корне). Это экономит rebuild scene graph в Vello — что на больших сценах 5-15ms на кадр. Поддержка Vello partial present ещё сильнее уменьшает GPU-время.
  • compileTimeCost: Средний. Диффинг display list — кодогенерация по командам (macro-generated match arms). Каждая новая команда добавляет дифф-логику; compile time растёт линейно.
  • tradeoffs: Требует детерминированной выдачи команд: один и тот же UI → один и тот же порядок DrawCmd. Анимации (transform, opacity) ломают reuse префикса — нужно явно выносить их в отдельный ‘ephemeral’ слой. Diff сам по себе O(ND) может доминировать при больших перестройках, тогда fallback — full rebuild.
  • existingUsage: Chrome’s cc::DisplayItemList + paint diff, Servo’s WebRender display list caching (SceneBuilder), Flutter’s Layer tree diffing, Makepad’s draw list reuse. Vello пока этого не имеет из коробки, но Scene API предполагает append/extend — естественное место для инкрементальной подачи.
  • rustFit: Хорошо. Display list уже flat — идеально для diff. Стабильный id команды = NodeId + slot index — простая арифметика, никаких хэшей строк. Через fload отдаём host-стороне список patches, host применяет к своему retained Scene.

Layout caching с per-node computed size (taffy-style)

Section titled “Layout caching с per-node computed size (taffy-style)”
  • idea: Вместо пересчёта layout с нуля при любом изменении — хранить layout tree в arena (Vec), каждая нода имеет cache entry {size, position, content_hash}. При invalidation: помечаем путь от изменённой ноды к корню + всех потомков. Layout-фаза: обход дерева, ноды с valid cache — O(1) skip, ноды dirty — пересчёт.
  • runtimeGain: Типичный UI: layout — 30-50% frame time. Caching убирает ~90% пересчётов: изменение текста в одном TextField = пересчёт 5-10 нод вместо всего дерева. Bevy + taffy показывает 60fps на сложном UI за счёт этого.
  • compileTimeCost: Низкий-средний. Требует проектирования вокруг arena и явного layout tree API. Сам taffy — зрелая библиотека, compile time посредственный.
  • tradeoffs: Arena фрагментируется при массовом create/destroy — нужен compaction или free list. Кэш размеров чувствителен к ‘flex’ изменениям (изменение размера родителя инвалидирует всё поддерево). Нужно явное разделение ‘intrinsic size’ и ‘final size’ — иначе каскадная инвалидация.
  • existingUsage: taffy (Dioxus, Bevy UI) — Block/Flexbox/Grid layout с явным dirty tracking на стороне клиента; Clay (C-библиотека, immediate-mode layout с arena); Yoga (Facebook) — классический retained layout с диффом.
  • rustFit: Отлично. Arena — Vec, никаких Box/RefCell. NodeId — generational index (совпадает с SlotMap техникой выше). Для fload: layout results — это массив (id, x, y, w, h), trivially serializable, никакого repr(C) не нужно.

Golden frame testing с perceptual hash (dHash/pHash/SSIM)

Section titled “Golden frame testing с perceptual hash (dHash/pHash/SSIM)”
  • idea: Для каждого golden-теста: рендерим кадр headless (Vello → CPU rasterization или wgpu offscreen), считаем perceptual hash (pHash или dHash) и сравниваем с эталоном. Threshold: distance < N — pass. Это ловит регрессии в рендере без fragile pixel-exact сравнения.
  • runtimeGain: В проде — ноль (это тестирование). В разработке — позволяет агрессивно менять pipeline (оптимизации layout, display list diff) без ручного визуального review. Можно ввести ‘perf budget’ assertions: время рендера кадра < X — иначе fail.
  • compileTimeCost: Низкий. Библиотеки маленькие. Само тестирование — это внешний harness, не часть hot path.
  • tradeoffs: pHash ловит структурные, но не цветовые регрессии (гамма, альфа-канал). Нужно комбинировать с pixel-exact diff для критичных областей (текст). Эталоны надо хранить — LFS или отдельный репо. Шум от GPU-драйверов: тот же код на Intel/AMD/NV может давать разные снимки на субпиксельном уровне — threshold подбирается экспериментально.
  • existingUsage: insta (snapshot testing в Rust), skia-safe + golden images в Servo, headless-gl в браузерном мире, dssim-core (Netflix SSIM), image-compare crate. Визуальная регрессия в egui — через скриншоты.
  • rustFit: Хорошо. Vello + wgpu offscreen — стандартный паттерн (используется в bevy для скриншотов). dHash тривиален (~30 строк). Через fload: тест-харнесс на host-side, плагин отдаёт кадр — host сравнивает.

Compile-time hashing для signal graph identity

Section titled “Compile-time hashing для signal graph identity”
  • idea: При компиляции UI-плагина вычисляется Merkle hash от его AST signal graph + типов событий. На hot-swap: если новый DLL имеет тот же root hash — state миграция не нужна, patch применяется in-place. Если hash изменился — используем под-деревья с совпадающими hash для частичной миграции состояния.
  • runtimeGain: Hot-swap плагина без state loss: типичное обновление (изменился один виджет) = Merkle path даёт нам точный список сохраняемых состояний. Время hot-swap: O(изменённые узлы), а не O(все виджеты). Это делает hot-reload пригодным для ежедневной разработки.
  • compileTimeCost: Средний. Макросы на вычисление хэша от типа/AST добавляют compile-time cost. Inventory даёт небольшой link-time overhead. Не доминирует.
  • tradeoffs: Хэш чувствителен к переименованиям и reordering полей — refactoring ломает state. Нужна ‘stable naming’ дисциплина в коде UI. Merkle-tree сам по себе — дополнительный кодоген, ~5-10% к размеру DLL.
  • existingUsage: Bevy Assets (AssetPath + hash → AssetId), rustc queries (DepNode = hash of query key), Buck2/Bazel для build cache. В UI: Makepad’s LiveId — hash от строки на этапе компиляции.
  • rustFit: Хорошо. Хэш вычисляется proc-macro на стороне plugin crate. Через fload передаётся u128 — тривиально. Соответствует ‘compiled signal graph’ ADR: идентичность известна на этапе компиляции, не нужен runtime reflection.

Pipeline stage caching — multi-level cache между layout → display list → GPU

Section titled “Pipeline stage caching — multi-level cache между layout → display list → GPU”
  • idea: Каждый этап pipeline (layout → display list → Vello scene → GPU commands) имеет свой кэш с независимой инвалидацией. Изменение цвета инвалидирует display list, но не layout. Изменение layout инвалидирует scene, но не source tree. Каждый уровень проверяет своё входное значение на equality — если совпало с кэшированным, downstream не дёргается.
  • runtimeGain: Ховер на кнопке: layout не меняется, только цвет. Должно занять 0.1ms (перерисовка одного draw cmd), а не 5ms (перестройка всего). Это центральная оптимизация для отзывчивости — perceived latency падает в 10-50x на типичных взаимодействиях.
  • compileTimeCost: Средний. Каждый cache level — отдельный тип. Композиция generic types увеличивает мономорфизацию, но линейно.
  • tradeoffs: Каждый уровень хранит своё состояние — растёт RSS (примерно 2-3x от минимального). Требует PartialEq на промежуточных представлениях, что может быть дорого для сложных типов (хэш вместо полного сравнения). При ошибке в equality — застревание stale данных, нужны golden-frame тесты для ловли.
  • existingUsage: rust-analyzer (SourceText → Parse → AstId → Hir), Servo (DOM → Style → Layout → DisplayList → WebRender scene), Chrome (Blink → cc layers → GpuCmds). Vello CPU-fallback имеет похожую layered архитектуру.
  • rustFit: Отлично. Каждый уровень — чистая функция с PartialEq на выходе. Совместимо с Salsa: каждый уровень = query. Через fload отдаётся display list — уже проверенный на equality с кэшем.

Topological ordering с priority inversion detection (petgraph::algo::toposort)

Section titled “Topological ordering с priority inversion detection (petgraph::algo::toposort)”
  • idea: Compiled signal graph — это DAG. Перед исполнением: toposort для определения порядка вычислений. Tarjan SCC для обнаружения циклов (signal A зависит от B, B от A — должно быть compile error). Каждому узлу присваивается topological level — изменения распространяются ‘волной’ снизу вверх, без лишних пересчётов.
  • runtimeGain: Избегаем повторного вычисления узла, если несколько его зависимостей изменились — порядок гарантирует один проход. Детекция циклов на этапе загрузки DLL, а не в рантайме — это критично для hot-swap безопасности.
  • compileTimeCost: Низкий. petgraph — зрелая библиотека, но тяжёлая (~30s к сборке). Можно заменить на ручной tarjan на ~100 строк.
  • tradeoffs: Циклы — это ошибка компиляции плагина, но при hot-swap нужно явно проверять (старый плагин + новый плагин могут в сумме дать цикл). Dynamic edge addition (редко, но бывает для conditional rendering) требует пересчёта toposort — можно инкрементально, но сложно.
  • existingUsage: petgraph::algo::toposort, petgraph::algo::tarjan_scc. Bevy render graph использует toposort для планирования passes. Buck2/Bazel — topological build execution.
  • rustFit: Отлично. Граф известен на этапе компиляции (compiled signal graph) — petgraph строится один раз при загрузке плагина, результат кэшируется. Через fload передаётся готовый Vec — порядок исполнения.

Vello retained scene + partial present (wgpu surface damage)

Section titled “Vello retained scene + partial present (wgpu surface damage)”
  • idea: Display list diff даёт нам damage rect. Передаём его в Vello через Scene::append только изменённых подсцен + на wgpu уровне используем partial present (Metal — presentsWithTransaction + dirty rects; Vulkan — VK_EXT_swapchain_maintenance1 с dirty regions; DX12 — DXGI_PRESENT_PARAMETERS). На неподдерживаемых платформах — scissor test на этапе rasterization.
  • runtimeGain: Скролл на 1px: GPU перерисовывает только полосу в 1px + newly-visible region. Экономия fill-rate в 10-100x на типичных UI операциях. На слабом GPU (Intel iGPU) это разница между 60fps и 15fps.
  • compileTimeCost: Низкий. Это конфигурация Vello и наша собственная подача данных. Никакой макрос-магии.
  • tradeoffs: Platform-specific код для partial present — на каждой ОС свой путь. Сложно отлаживать: damage rect bug = артефакты на экране. Полная перерисовка иногда быстрее, чем 100 маленьких partial updates — нужен merge порог. Vello active development — API меняется.
  • existingUsage: wgpu TextureView::begin_render_pass с viewport, Vello Scene::append с transform, Chrome’s partial swap (DAMAGE_EXT / VK_KHR_swapchain_mutable_format). DirectComposition на Windows, Metal partial redraw на macOS/iOS.
  • rustFit: Хорошо, но требует расширения Vello: Vello не имеет retained mode из коробки, мы строим его поверх (Scene::reset + append только dirty подсцен). wgpu partial present пока нестабилен — нужен fork или feature flag. Для fload: host-side Vello instance, plugin отдаёт draw commands с damage rect.

Compiled signal graph diffing — патч на уровне AST

Section titled “Compiled signal graph diffing — патч на уровне AST”
  • idea: Proc-macro #[derive(Signal)] анализирует AST: для каждого поля генерирует отдельный signal node + function ‘apply_patch(old, new) -> Option’. При hot-swap: старое состояние передаётся в новый плагин через fload как сериализованный блоб, новый код пытается применить его через совпадающие поля (по имени + типу). Несовпадающие — default.
  • runtimeGain: Hot-reload плагина за 10-50ms: сериализация состояния + десериализация + применение только изменённых полей. Без этой техники hot-swap требует либо полного рестарта UI (плохой UX), либо сложного ручного миграционного кода.
  • compileTimeCost: Низкий. Макрос на анализ AST небольшой. Основной cost — на стороне кодгена.
  • tradeoffs: Сериализация состояния — это contract между версиями плагина. Переименование поля = потеря состояния. Нужна явная политика ‘stable field names’ + linter. Bincode не даёт schema evolution, rkyv — даёт, но сложнее. Для сложных типов (Vec) нужен отдельный diff, иначе полный сериализованный список = тяжёлый hot-swap.
  • existingUsage: Leptos server functions, Dioxus hot reload (через subsecond + dioxus-devtools), Makepad Live DSL, SwiftUI @State, Compose Stable annotations.
  • rustFit: Отлично. Соответствует ADR ‘compiled signal graph’. Patch format — bincode/rkyv через fload, никакого repr(C). Derive-макрос генерирует всё на этапе компиляции — zero runtime reflection.

Priority-based frame scheduling (input > anim > layout > render)

Section titled “Priority-based frame scheduling (input > anim > layout > render)”
  • idea: На каждый кадр назначаем priority: user-input > animation > layout > render. Salsa queries помечаются durability: user-input = lowest durability (инвалидируется всегда), layout = medium, scene build = high. Scheduler выбирает: в бюджете 8ms — исполняем только high-priority + user-input. Low-priority (prefetch, thumbnail decode) откладывается.
  • runtimeGain: В бюджете 24fps (41ms) всё успевает. В бюджете 60fps (16ms) — нет. Priority scheduling позволяет держать 60fps на hot path, откладывая второстепенное. Perceived latency падает: input → screen за 16ms, даже если под капотом layout ещё не доделан (показываем прошлый кадр с transform).
  • compileTimeCost: Низкий. Макросный codegen на уровне типов.
  • tradeoffs: Требует явной классификации всех задач по приоритету — ошибка классификации = jank. Откладывание low-priority может привести к starvation (на слабом железе prefetch никогда не выполняется) — нужен aging механизм. Сложно отлаживать: race между priority levels = визуальные артефакты.
  • existingUsage: Bevy change detection (Ticks), Salsa durability (серый = freeze на N revisions), React Fiber lanes, SwiftUI AttributeGraph (priority propagation).
  • rustFit: Хорошо. Приоритеты — это дополнительное поле в NodeId/Query key. Tokio::task::Priority (если используется) или ручной очередью. Совместимо с current_thread runtime: один поток, явная очередь по приоритетам.

Notes: Привязка к уже решённым ADR-пунктам: compiled signal graph, Elm-outside, display-list как backend contract, Vello+Parley, fload как DLL boundary, frame budget 24/45/60-75 fps, LTO/PGO/codegen-units=1. Эти техники diffing/caching/invalidation сидят поверх этих решений. Главные связки: (1) Salsa + interning — на входе pipeline, до layout; (2) SlotMap + generational IDs — кости деревьев; (3) bitset dirty + coarse damage tracking — между layout и display list; (4) display list diff + retained Vello scene — на выходе; (5) structural sharing — только для больших списков (message list), не для общего случая. Не делать: fine-grained signal tracking в рантайме (уже отвергнуто), Virtual DOM diffing как React (подходит хуже, чем slot map + bitset dirty для retained UI), межфреймовое удержание Arc без ownership (мешает PGO/BOLT и раздувает hot path). Файлы: /d/porn/gay/bdsm/theferaproject/context/OPTIM-IDEAS-4-oq24.md, OPTIM-IDEAS-5.md прочитаны — там runtime/plugin, ничего про UI diffing.