Использование плагина Immersive Translate с LLM для перевода некоторых сайтов: заметил(а) подтормаживания мыши, несколько советов от LLM

Кроме того, фризы воспроизводятся только на некоторых сайтах. Я попросил LLM пробежаться по коду плагина; позже попрошу опытного человека посмотреть, есть ли идеи по оптимизации.

С точки зрения архитектуры и реализации разработки плагина, для глубокої оптимизации узких мест производительности, вызванных LLM‑переводом, можно идти по следующим ключевым направлениям:

  1. Внедрение асинхронной архитектуры обработки (Web Workers)
    Сейчас большое количество регулярных сопоставлений, HTML‑парсинга и сравнения переведённого текста, вероятно, выполняется в Content Script (главном потоке).
  • Решение по оптимизации: перенести логику, не связанную с прямыми DOM‑операциями (например, разбор streaming‑протокола LLM, предобработка текста, алгоритмы сопоставления многоязычных версий), в Web Worker.
  • Эффект: разгрузить главный поток, чтобы браузер, обрабатывая данные перевода, всё равно мог с 60fps отзываться на прокрутку и клики мышью.
  1. «Пакетная отрисовка» и «временное нарезание» (Scheduling)
    При потоковом выводе (Streaming) хуже всего обновлять DOM при получении каждого символа.
  • Решение по оптимизации:
    • Буферизация обновлений (Buffering): задать буфер 100–200 мс, объединять несколько фрагментов, возвращаемых LLM, и затем инициировать одно DOM‑обновление через requestAnimationFrame.
    • Временное нарезание (Time Slicing): если нужно разом вставить большое количество узлов перевода, использовать идею наподобие React Fiber — разбить задачу на множество мелких подзадач и вставлять их по одной в моменты простоя браузера (requestIdleCallback).
  • Эффект: превратить исходно плотные перерасчёты (Reflow) в ритмичные обновления и существенно снизить пиковую нагрузку на CPU.
  1. «Ленивая отрисовка» на основе видимой области (Intersection Observer)
    Если страница очень длинная, полный перевод приводит к тому, что тысячи DOM‑узлов одновременно обновляются в фоне, даже если они не на экране.
  • Решение по оптимизации: использовать API IntersectionObserver.
    • Запускать запрос перевода и монтировать DOM перевода только когда узел оригинала входит в видимую область (или скоро войдёт).
    • Для узлов перевода, которые находятся далеко вне видимой области, можно временно уничтожать или скрывать их, чтобы снизить давление на вычисления раскладки в браузере.
  • Эффект: снизить объём вычислений с «масштаба всей страницы» до «масштаба экрана».
  1. Оптимизация стратегии монтирования DOM (Containment)
    При вставке узлов перевода, если нарушается исходная раскладка, это может привести к перерасчёту всей страницы.
  • Решение по оптимизации:
    • CSS Containment: добавить для контейнера после перевода contain: layout; или contain:
      content; Это сообщит браузеру, что внутренние изменения этого узла не влияют на внешнюю раскладку.
    • Фиксированный плейсхолдер: до начала перевода оценить высоту по длине исходного текста и зарезервировать место, чтобы при потоковом «подпрыгивании» текста перевода страница не «дрожала» из‑за постоянных изменений.
  • Эффект: ограничить область перерасчёта, чтобы не получалось «потянешь за ниточку — дёрнется всё».
  1. Снижение чрезмерных срабатываний MutationObserver
    Поскольку сам плагин изменяет DOM, он снова триггерит собственное наблюдение MutationObserver.
  • Решение по оптимизации:
    • Когда плагин вставляет узлы перевода, через глобальный флаг (Flag) временно отключать наблюдение, либо в слушателе отфильтровывать узлы, созданные самим плагином, через node.isTrusted
      или по специфическим атрибутам.
    • Динамически повышать порог mutationChangeDelay: когда обнаруживается, что пользователь быстро прокручивает страницу, автоматически увеличивать задержку или приостанавливать сканирование.
  1. Для streaming‑вывода — «инкрементальные обновления», а не «полная замена»
  • Решение по оптимизации: если узел перевода — это контейнер, то при потоковом обновлении следует лишь дописывать текстовые узлы, а не каждый раз делать innerHTML = …
    и заново пересобирать внутреннюю структуру.
  • Эффект: уменьшить нагрузку на сборку мусора (GC) и накладные расходы на разбор DOM‑дерева.

Итог
Самый эффективный путь оптимизации: Web Worker обрабатывает данные →
буферизированная отрисовка через requestAnimationFrame →
ограничение области через IntersectionObserver →
изоляция раскладки через CSS contain. Так можно идеально развязать «тяжёлую нагрузку» от LLM и «плавность» браузера.


Когда включаю перевод LLM, загрузка CPU мгновенно подскакивает с 5 до 50.