---
url: https://nsh.dev/ru/docs/performance-report
description: "Измеренные показатели приложений на NSH: стоимость commit и кадров по базовым замерам NSH, а также частота кадров, нагрузка на CPU и потребление памяти в галерее по страницам на Linux, macOS, Windows и iOS, с описанием того, как их воспроизвести."
---

# Отчёт о производительности

Сколько стоят приложения NSH, в измерениях. Как NSH обеспечивает их быстродействие и как измерить собственное приложение, описано в разделе [Производительность](https://nsh.dev/translations/ru/docs/performance.md).

<a id="baselines"></a>

## Базовые показатели

По собственным замерам NSH (Apple silicon, если не указано иное):

| Сценарий | Результаты |
|---|---|
| Приложение-счётчик, headless (debug) | Первый кадр ~0,5–0,65 мс (58 операций / 49 элементов монтирования); одно нажатие ~0,15 мс |
| Приложение-счётчик, Mac Catalyst (release) | Первый кадр ~1,05–1,15 мс (около 0,7 мс уходит на первую раскладку 10 текстов через CoreText); одно нажатие ~0,12 мс |
| Список фото в Gallery, headless (debug) | Первый кадр: 653 операции / 224 узла, ~2,7 мс |
| Прокрутка списка фото, Catalyst (release) | Перепривязка одной строки при медленной прокрутке ~0,4 мс; целый экран из ~32 строк при быстром броске 0,9–1,6 мс |
| Прокрутка списка фото, эмулятор Android (arm64, release) | Перепривязка одной строки ~0,3–0,4 мс (JS ~60 мкс) |
| Запуск движка (release) | ~75–155 мкс; глобальные API (`fetch`, `URL`, ...) ничего не стоят до первого использования, затем ~90 мкс |
| Hot reload | ~120 мс от сохранения файла до запуска новой версии; применение обновления 0,2–0,5 мс |

## Linux (GTK 4)

Замеры выполнены на настольном компьютере с Linux (x86_64, 24 потока, GPU NVIDIA), release-сборки, Gallery в окне 1896 × 1030 на дисплее 120 Гц; каждое значение снято автоматически командой `examples/gallery/tools/perf.py` (`nsh test --target linux --fps`, одна строка в секунду; CPU показан для всего процесса, в процентах от одного ядра). *Frame p95* — время главного потока на один отрисованный кадр на 95-м процентиле: если оно меньше 8,3 мс, кадр укладывается в период обновления 120 Гц.

<a id="animated-demos"></a>

### Анимированные демо

<div class="data-table">

| Демо | FPS | Frame p95 | CPU |
|---|---|---|---|
| Stocks: 22 живых спарклайна, внутридневной график, книга заявок, лента сделок | 117–120 | 5–6 мс | 25–29% (1200 × 800), 28–32% (1896 × 1030) |
| Stocks, окно на весь дисплей 4K 144 Гц при масштабе 1,6× | 143–145 | 4,1–4,4 мс | 26–29% |
| Canvas: индикатор, часы, точечная матрица | 120 | 2,6–4,4 мс | 5–10% |
| Animations: три нативных цикла, пружины, последовательности | 120 | 2,0 мс | 12–13% |

</div>

<a id="every-page"></a>

### Каждая страница

Каждая страница Gallery при открытии и во время прокрутки тестом, в окне 1200 × 800 на дисплее 144 Гц (на той же машине). NSH рисует кадр только тогда, когда на экране что-то меняется, поэтому *FPS* здесь — это частота в то время, пока что-то движется: один кадр на медианный интервал между отрисованными кадрами (`perf.py`, `moving`); при 144 Гц дисплея каждый кадр анимации или прокрутки приходит через одно обновление после предыдущего. *Дольше одного обновления* — доля кадров, которые заняли больше времени, чем одно обновление (6,9 мс). GIF и WebP на странице Images воспроизводятся с собственной частотой кадров (здесь около 50 FPS), так же как и видео. Страницы Physics, Overlays и Menus короткие: в окне 1200 × 800 прокрутка доходит до их конца за несколько кадров, поэтому они измерены в окне 900 × 500, где есть место для прокрутки.

<div class="data-table">

| Страница | Источник кадров | FPS | Frame p95 | Дольше одного обновления | CPU |
|---|---|---|---|---|---|
| Stocks | Живые данные, нативные циклы | 144 | 4,1 мс | 0% | 26% |
| Stocks, прокрутка | Живые данные, прокрутка | 144 | 4,1 мс | 0% | 25% |
| Photos, прокрутка | Прокрутка | 144 | 2,8 мс | 2% | 9% |
| Lists, прокрутка | Прокрутка | 143 | 1,7 мс | 0% | 7% |
| Scrolling, прокрутка | Прокрутка | 144 | 1,6 мс | 1% | 4% |
| Navigation, прокрутка | Прокрутка | 143 | 1,7 мс | 0% | 2% |
| Form, прокрутка | Прокрутка | 144 | 1,3 мс | 0% | 4% |
| Resizable, прокрутка | Прокрутка | 143 | 0,6 мс | 0% | 1% |
| Dock, прокрутка | Прокрутка | 142 | 0,5 мс | 1% | 1% |
| Gestures | Пружины, пульсирующее кольцо | 144 | 1,6 мс | 0% | 1–5% |
| Physics, прокрутка | Прокрутка (окно 900 × 500) | 144 | 1,6 мс | 1% | 1% |
| Animations | Нативные циклы | 144 | 2,6 мс | 0–1% | 5% |
| Canvas, прокрутка | Прокрутка, нативные циклы | 144 | 4,2 мс | 0% | 1–4% |
| Styles, прокрутка | Прокрутка | 144 | 2,3 мс | 2% | 3% |
| Tailwind | Утилиты `animate-*` | 144 | 1,7 мс | 0% | 3–8% |
| Text, прокрутка | Прокрутка | 144 | 1,9 мс | 0% | 4% |
| Text View, прокрутка | Прокрутка | 141 | 1,8 мс | 2% | 7% |
| Images, прокрутка | Прокрутка, кадры GIF и WebP | 143 | 2,9 мс | 2% | 8% |
| Components, прокрутка | Прокрутка | 144 | 1,4 мс | 0% | 3% |
| Overlays, прокрутка | Прокрутка (окно 900 × 500) | 144 | 1,7 мс | 0% | 2% |
| Menus, прокрутка | Прокрутка (окно 900 × 500) | 144 | 1,9 мс | 0% | 2% |
| Controls | Переходы | 144 | 0,9 мс | 0% | 1–4% |
| Web and video | Кадры видео, страница | 135–142 | 1,0 мс | 0% | 7–8% |

</div>

<a id="memory"></a>

### Память

В покое, через 3 с после запуска (release-раннеры, окно 480 × 640). *Private* — память, которой владеет только сам процесс (USS); *resident* добавляет страницы разделяемых библиотек (GTK, драйвер GPU), которые совместно используют все приложения GTK на машине:

<div class="data-table">

| Приложение | Рендерер | Private (USS) | Resident (RSS) |
|---|---|---|---|
| Приложение на Rust (`nsh init --template rust`) | GL (`ngl`) | ~49 МБ | ~142 МБ |
| Приложение на Rust | Cairo (`GSK_RENDERER=cairo`) | ~28 МБ | ~95 МБ |
| Счётчик на TSX (`examples/counter`) | GL | ~79 МБ | ~166 МБ |
| Окно GTK 4 с одной меткой, для сравнения | Vulkan / Cairo | ~62 / ~40 МБ | ~132 / ~109 МБ |

</div>

Повторное посещение страницы не добавляет памяти; первое посещение страницы увеличивает процесс на то, что он намеренно кэширует (декодированные изображения, не более 128 МБ), и на библиотеки, которые загружаются при первом использовании (WebKitGTK для веб-представлений).

<a id="app-size"></a>

### Размер приложения

Приложение hello world, созданное командой `nsh init` с каждым шаблоном, и два приложения, которыми пользуются люди, в том виде, как `nsh package --target linux` собирает их для x86_64. Раннер — единственный исполняемый файл приложения; архив — это `.tar.gz`, в котором оно поставляется. Hello world собраны с оптимизацией на этапе компоновки (link-time optimization) и со снятыми символами (`CARGO_PROFILE_RELEASE_LTO=fat`, `CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1`, `CARGO_PROFILE_RELEASE_STRIP=true`):

<div class="data-table">

| Приложение | Язык | Раннер | Архив |
|---|---|---|---|
| Hello world (`--template rust`) | Rust | 16,5 МБ | 7,5 МБ |
| Hello world (`--template ts`) | TSX, на QuickJS | 25,7 МБ | 11,6 МБ |
| Hello world (`--template mixed`) | TSX и крейт Rust | 25,8 МБ | 11,7 МБ |
| [sysmon](https://github.com/huacnlee/sysmon) 0.1.3 | Rust | 20,9 МБ, со снятыми символами | 9,7 МБ |
| [Omasend](https://github.com/huacnlee/omasend) 0.2.0 | Rust | 22,3 МБ | 9,8 МБ |

</div>

Раннеры TSX содержат среду выполнения QuickJS, нативные модули и рендерер; собственный код приложения — это его бандл рядом с раннером, для hello world меньше 1 КБ.

Что определяет эти цифры:

- **Рендерер**: с драйвером NVIDIA NSH рисует через GL-рендерер GSK, кадры которого обходятся в долю от стоимости кадров рендерера Vulkan (демо Animations: ~1 мс отрисовки на кадр вместо ~9,5 мс). Выбор при этом по-прежнему остаётся за `GSK_RENDERER`.
- **Растеризация Canvas**: каждый canvas растеризуется один раз в тайлы 256 px, которые хранятся на GPU. Анимированное значение перерисовывает только ту область, которую покрывают читающие его операции, поверх сохранённых пикселей статической части рисунка; canvas вне поля зрения ждёт, пока его не прокрутят в видимую область.
- **Пути, отложенные один раз**: `fillRect` и `fillText` после длинного пути оставляют его на месте, пока он снова не понадобится (линия из 400 точек, затем 240 столбцов: 0,63 мс).

## macOS (AppKit)

Замеры выполнены на MacBook Pro (Apple M3, 24 ГБ) на встроенном дисплее ProMotion 120 Гц, release-сборки, Gallery в окне 1200 × 800; каждое значение снято автоматически командой `examples/gallery/tools/perf.py` (`nsh test --target macos
--fps`, одна строка в секунду; CPU показан для всего процесса, в процентах от одного ядра, GPU — его доля от GPU). Фазы — это время главного потока на кадр: *tick* (нативные анимации display link) и *canvas* (воспроизведение рисунков).

<a id="animated-demos-1"></a>

### Анимированные демо

<div class="data-table">

| Демо | FPS | Frame p95 | CPU | GPU |
|---|---|---|---|---|
| Stocks: 22 живых спарклайна, внутридневной график, книга заявок, лента сделок | 120 | 2,7–3,7 мс | 33–36% | 18–22% |
| Canvas: индикатор, часы, точечная матрица | 119–120 | 0,6–0,7 мс | 4–7% | 0% |
| Animations: три нативных цикла, пружины, последовательности | 119–120 | 0,3–1,6 мс | 5–8% | 0% |

</div>

На Stocks время главного потока на кадр составляет ~1,6 мс на тики, из которых ~0,45 мс уходит на воспроизведение анимированных частей canvas; остальное в каждом canvas Core Animation рисует в собственных потоках.

<a id="every-page-1"></a>

### Каждая страница

Каждая страница Gallery при открытии и во время прокрутки тестом, в том же окне 1200 × 800. Как и на Linux, *FPS* — это частота в то время, пока что-то движется (один кадр на медианный интервал между отрисованными кадрами); при 120 Гц дисплея каждый кадр анимации или прокрутки приходит через одно обновление после предыдущего. *Дольше одного обновления* — доля кадров, которые заняли больше времени, чем одно обновление (8,3 мс). Страница в покое не рисует кадров и использует 2–5% CPU. GIF и WebP на странице Images воспроизводятся в Core Animation, а видео — в AVFoundation, с собственной частотой кадров.

<div class="data-table">

| Страница | Источник кадров | FPS | Frame p95 | Дольше одного обновления | CPU |
|---|---|---|---|---|---|
| Stocks | Живые данные, нативные циклы | 120 | 3,2 мс | 0% | 35% |
| Stocks, прокрутка | Живые данные, прокрутка | 120 | 3,5 мс | 0% | 36% |
| AI Chat, прокрутка | Прокрутка | 120 | 2,4 мс | 1% | 5% |
| Photos, прокрутка | Прокрутка | 120 | 1,4 мс | 0% | 10% |
| Lists, прокрутка | Прокрутка | 120 | 1,6 мс | 0% | 8% |
| Scrolling, прокрутка | Прокрутка | 120 | 2,7 мс | 0% | 6% |
| Navigation, прокрутка | Прокрутка | 120 | 0,2 мс | 0% | 4% |
| Form, прокрутка | Прокрутка | 120 | 0,6 мс | 0% | 5% |
| Resizable, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 4% |
| Dock, прокрутка | Прокрутка | 120 | 0,3 мс | 1% | 5% |
| Gestures | Пружины, пульсирующее кольцо | 120 | 0,4 мс | 0% | 4% |
| Physics, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 4% |
| Animations | Нативные циклы | 120 | 0,3 мс | 0% | 5% |
| Canvas, прокрутка | Прокрутка, нативные циклы | 120 | 1,2 мс | 0% | 7% |
| Styles, прокрутка | Прокрутка, покачивающееся преобразование | 120 | 1,0 мс | 0% | 6% |
| Tailwind | Утилиты `animate-*` | 120 | 0,5 мс | 0% | 4% |
| Text, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 5% |
| Text View, прокрутка | Прокрутка | 120 | 0,7 мс | 0% | 5% |
| Images, прокрутка | Прокрутка | 120 | 0,4 мс | 0% | 5% |
| Components, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 5% |
| Overlays, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 4% |
| Menus, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 5% |
| Controls | Переходы | 120 | 0,3 мс | 0% | 4% |
| Web and video, прокрутка | Прокрутка | 120 | 0,2 мс | 0% | 6% |
| System | `requestAnimationFrame`, таймеры | 120 | 0,2 мс | 0% | 3% |
| Standard APIs, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 5% |
| Storage, прокрутка | Прокрутка | 120 | 0,3 мс | 0% | 5% |

</div>

<a id="memory-1"></a>

### Память

В покое, через 3 с после запуска (release-раннеры). *Footprint* — память, которую macOS засчитывает процессу (то, что показывает Activity Monitor); *resident* добавляет страницы разделяемых библиотек системы:

<div class="data-table">

| Приложение | Окно | Footprint | Resident (RSS) |
|---|---|---|---|
| Приложение на Rust (`nsh init --template rust`) | 1200 × 800 | ~19 МБ | ~86 МБ |
| Счётчик на TSX (`examples/counter`) | 1200 × 800 | ~20 МБ | ~89 МБ |
| Окно AppKit с одной меткой, для сравнения | 480 × 640 | ~17 МБ | ~80 МБ |

</div>

В ходе обхода Gallery процесс занимает 84–230 МБ до страницы Canvas и ~350–360 МБ начиная со страницы Text View — с учётом того, что он намеренно кэширует (декодированные изображения, не более 128 МБ; документы страницы Text View), и фреймворков, которые загружаются при первом использовании (WebKit для веб-представлений); растры страницы Canvas, до ~310 МБ при трёх canvas на экране одновременно, освобождаются при закрытии страницы.
<a id="app-size-1"></a>

### Размер приложения

Hello world-приложения, созданные `nsh init` с каждым шаблоном, в том виде, в каком `nsh package --target macos` собирает их для Apple
silicon (arm64) с оптимизацией на этапе компоновки и удалением символов, как на Linux, а также два приложения выше в том виде, в каком они поставляются для Apple
silicon. Исполняемый файл — это раннер внутри `.app`; zip — это `.app` в сжатом виде (`ditto -c -k`):

<div class="data-table">

| Приложение | Язык | Исполняемый файл | В zip `.app` |
|---|---|---|---|
| Hello world (`--template rust`) | Rust | 12,4 МБ | 6,0 МБ |
| Hello world (`--template ts`) | TSX на QuickJS | 19,8 МБ | 9,5 МБ |
| Hello world (`--template mixed`) | TSX и крейт Rust | 19,8 МБ | 9,5 МБ |
| [sysmon](https://github.com/huacnlee/sysmon) 0.1.3 | Rust | 21,6 МБ | 8,4 МБ (`.tar.gz` его релиза) |
| [Omasend](https://github.com/huacnlee/omasend) 0.2.0 | Rust | 18,4 МБ | 9,7 МБ (`.tar.gz` его релиза) |

</div>

Чем обусловлены эти цифры:

- **Кадры только при изменениях**: display link тикает, пока идёт нативная анимация, и останавливается вместе с ней; анимация
  останавливается вместе со своей страницей (удаление её представления завершает его keyframes, цикл останавливается в `cleanup` своего компонента).
- **Canvas рисуются там, где это дешевле всего**: canvas, который перерисовывается часто (анимация, живые данные), рисуется в потоках Core
  Animation (`drawsAsynchronously`), что освобождает главный поток; canvas, который перерисовывается время от времени, рисуется
  сразу, без графической памяти, которую удерживает асинхронная отрисовка. Анимированное значение перерисовывает только ту область, которую
  покрывают операции, читающие это значение.
- **Статичные части canvas сохраняются**: когда рисунок остаётся неизменным, длинная часть перед первой анимированной операцией
  сохраняется как слой; каждый кадр рисует этот слой и воспроизводит заново только анимированный остаток, сразу в главном потоке (gauge, часы и точечная матрица
  на странице Canvas: никакой работы GPU и вдвое меньше памяти).
- **Содержимое рисуется при изменении**: тексты и canvas рисуются в собственные слои и перерисовываются, когда меняются их содержимое или
  размер, а не когда они перемещаются или прокручиваются.

## Windows (Win32, Direct2D)

Измерения проведены на настольном компьютере с Windows 11 (Intel Core i7-13700KF, 32 ГБ, NVIDIA GeForce RTX 3060 Ti) на 4K-дисплее 144 Гц с масштабом
150%, релизные сборки, галерея в окне 1200 × 800 (1800 × 1200 пикселей), каждое показание снималось автоматически
с помощью `examples/gallery/tools/perf.py` (`nsh test --target windows --fps`, одна строка в секунду; CPU — весь процесс, в
процентах от одного ядра). Фазы — это работа главного потока за кадр: *paint* (Direct2D рисует окно) и, внутри
неё, *canvas* (запись и воспроизведение рисунков).

<a id="animated-demos-2"></a>

### Анимированные демо

<div class="data-table">

| Демо | FPS | Кадр p95 | Paint / canvas за кадр | CPU |
|---|---|---|---|---|
| Stocks: 22 живых спарклайна, внутридневной график, книга заявок, лента сделок | 144 | 3,7–4,3 мс | 1,4 / 0,13 мс | 36–43% |
| Canvas: gauge, часы, точечная матрица | 144 | 1,0–2,0 мс | 0,8–1,4 / 0,05 мс | 21–35% |
| Animations: три нативных цикла, пружины, последовательности | 144 | 1,0–3,6 мс | 0,9–1,9 мс | 20–41% |

</div>

Stocks выполняет коммиты 16–17 раз в секунду, каждый — до 2,1 мс в JS и 1,7 мс в ядре в потоке движка (p95).

> [!NOTE]
> Страницы, анимации которых перемещают представления по большим областям, здесь потребляют больше CPU, чем на Linux и macOS (Animations 20–41%
> против 5–13%, Gestures 36% против 1–5%). Кадр в Windows перерисовывает средствами Direct2D в главном потоке каждое представление
> в изменившихся областях: растущее кольцо перерисовывает карточку под собой в каждом кадре. В macOS Core Animation
> перемещает, масштабирует и затеняет сохранённые слои на GPU, не перерисовывая их, а в Linux GSK сохраняет
> render nodes того, что не изменилось. Кроме того, в цифрах учтены 144 кадра в секунду при 1800 × 1200 пикселей.

<a id="every-page-2"></a>

### Все страницы

Все страницы галереи: при открытии и пока тест их прокручивает, в том же окне 1200 × 800. Как и на Linux и
macOS, *FPS* — это частота, пока что-то движется (один кадр на медианный интервал между отрисованными кадрами); при 144 Гц дисплея
каждый кадр анимации или прокрутки приходит через одно обновление после предыдущего. *Дольше одного обновления* — доля кадров,
которые заняли больше времени, чем одно обновление (6,9 мс). Страница в покое не рисует кадров и использует 0–2% CPU. GIF и WebP на странице Images воспроизводятся
с собственной частотой кадров (здесь около 40 FPS). Physics, Overlays, Menus и Storage измерены в окне 900 × 500,
где им есть куда прокручиваться.

<div class="data-table">

| Страница | Источник кадров | FPS | Кадр p95 | Дольше одного обновления | CPU |
|---|---|---|---|---|---|
| Stocks | Живые данные, нативные циклы | 144 | 3,9 мс | 0% | 36% |
| Stocks, прокрутка | Живые данные, прокрутка | 144 | 4,3 мс | 0% | 43% |
| Photos, прокрутка | Прокрутка | 143 | 2,1 мс | 0% | 9% |
| Lists, прокрутка | Прокрутка | 144 | 2,0 мс | 1% | 19% |
| Scrolling, прокрутка | Прокрутка | 143 | 2,1 мс | 0% | 7% |
| Navigation, прокрутка | Прокрутка | 144 | 0,8 мс | 0% | 9% |
| Form, прокрутка | Прокрутка | 144 | 0,8 мс | 0% | 12% |
| Resizable, прокрутка | Прокрутка | 143 | 0,4 мс | 0% | 4% |
| Dock, прокрутка | Прокрутка | 144 | 0,5 мс | 1% | 5% |
| Gestures | Пружины, пульсирующее кольцо | 144 | 1,8 мс | 0% | 36% |
| Physics, прокрутка | Прокрутка (окно 900 × 500) | 144 | 0,8 мс | 0% | 4% |
| Animations | Нативные циклы | 144 | 1,0 мс | 0% | 28% |
| Canvas, прокрутка | Прокрутка, нативные циклы | 144 | 2,0 мс | 0% | 35% |
| Styles, прокрутка | Прокрутка, покачивающееся преобразование | 144 | 2,1 мс | 0% | 39% |
| Tailwind | Утилиты `animate-*` | 144 | 0,6 мс | 0% | 19% |
| Text, прокрутка | Прокрутка | 144 | 1,6 мс | 0% | 8% |
| Text View, прокрутка | Прокрутка | 144 | 1,1 мс | 0% | 10% |
| Images, прокрутка | Прокрутка, кадры GIF и WebP | 143 | 4,8 мс | 2% | 23% |
| Components, прокрутка | Прокрутка | 144 | 1,0 мс | 1% | 11% |
| Overlays, прокрутка | Прокрутка (окно 900 × 500) | 144 | 0,6 мс | 0% | 1% |
| Menus, прокрутка | Прокрутка (окно 900 × 500) | 144 | 0,6 мс | 0% | 2% |
| Controls | Переходы | 117–144 | 1,5 мс | 0% | 22% |
| Web and video, прокрутка | Прокрутка | 144 | 1,6 мс | 0% | 15% |
| System | `requestAnimationFrame`, таймеры | 144 | < 0,1 мс | 0% | 1% |
| Storage, прокрутка | Прокрутка (окно 900 × 500) | 144 | 0,5 мс | 0% | 1% |

</div>

<a id="memory-2"></a>

### Память

В покое, через 3 с после запуска (релизные раннеры, окно 1200 × 800). *Приватный рабочий набор* — это память, которую процесс
использует один (столбец «Память» в Диспетчере задач); *приватные байты* — то, что он зафиксировал; *рабочий набор* добавляет страницы
системных библиотек, которые он делит с другими процессами (Direct3D, Direct2D, DirectWrite, драйвер GPU):

<div class="data-table">

| Приложение | Приватный рабочий набор | Приватные байты | Рабочий набор |
|---|---|---|---|
| Приложение на Rust (`nsh init --template rust`) | ~14 МБ | ~60 МБ | ~53–71 МБ |
| Счётчик на TSX (`examples/counter`) | ~15 МБ | ~61 МБ | ~56–71 МБ |

</div>

За время тура по галерее рабочий набор составляет ~108–115 МБ на Stocks, ~230 МБ начиная со страницы Canvas, ~320–340 МБ начиная со
страницы Text View и ~390–405 МБ после того, как Web and video откроет WebView2 (чьи страницы работают в собственных процессах WebView2),
вместе с тем, что он кэширует намеренно (декодированные изображения, не более 128 МБ; документы страницы Text View).

<a id="app-size-2"></a>

### Размер приложения

Hello world-приложения, созданные `nsh init` с каждым шаблоном, в том виде, в каком `nsh package --target windows` собирает их для x86_64
(раннеры на Rust с оптимизацией на этапе компоновки и удалением символов, как на Linux). Исполняемый файл — это `<Name>.exe` приложения;
zip — это `.zip`, записанный рядом с его каталогом:

<div class="data-table">

| Приложение | Язык | Исполняемый файл | Zip |
|---|---|---|---|
| Hello world (`--template rust`) | Rust | 15,0 МБ | 6,7 МБ |
| Hello world (`--template ts`) | TSX на QuickJS | 28,6 МБ | 12,4 МБ |
| Hello world (`--template mixed`) | TSX и крейт Rust | 26,3 МБ | 11,5 МБ |

</div>

Чем обусловлены эти цифры:

- **Кадры только при изменениях и рисуются только там, где изменилось**: тикер, синхронизированный с композитором, работает, пока идёт нативная
  анимация, переход или callback кадра. Кадр рисует области изменившихся представлений (где они
  были и где находятся), поверх пикселей, которые хранит окно, и передаёт композитору только их (Stocks:
  ~34 представления за кадр).
- **Canvas записывают свою анимированную часть**: рисунок записывается как списки команд Direct2D, векторный и чёткий при любом
  масштабе. Когда анимированное значение меняется, заново записываются только операции, начиная с первой, которая читает анимированное значение; то,
  что идёт до неё, сохраняется (Stocks: ~26 мс записи в секунду для 22 пульсирующих спарклайнов и
  графика).
- **Рисуется только то, что видно**: дочерние элементы scroll view или clipping view, находящиеся вне поля зрения, исключаются из
  кадра, а представления без собственного содержимого рисуют только фон и границу.

## iOS (UIKit)

Измерения проведены в симуляторе iOS (iPhone 18 Pro Max, на MacBook Pro выше), релизные сборки, с тем же туром
(`perf.py --target ios`: `nsh test --target ios --fps`, касания пальца в том виде, в каком их получает UIKit). Симулятор выводит изображение
с частотой 60 Гц; на iPhone с экраном ProMotion display link в NSH работают на частоте экрана 120 Гц (приложения, которые `nsh package`
собирает, и хост разработки запрашивают её через `CADisableMinimumFrameDurationOnPhone`). Фазы — это *tick* (нативные
анимации), *drain* (применение пакетов движка) и *scroll* (то, что следует за scroll view: списки, sticky-ячейки,
привязанные значения).

<div class="data-table">

| Страница | Источник кадров | FPS | Кадр p95 | CPU |
|---|---|---|---|---|
| Home, прокрутка | Прокрутка | 60 | 0,1 мс | 6% |
| Stocks | Живые данные, нативные циклы | 60 | 0,3 мс | 22% |
| Stocks, прокрутка | Живые данные, прокрутка | 60 | 0,3 мс | 25% |
| Photos, прокрутка | Прокрутка | 60 | 1,2 мс | 6% |
| Lists, прокрутка | Прокрутка | 60 | 1,1 мс | 8% |
| Scrolling, прокрутка | Прокрутка | 60 | 0,1 мс | 7% |
| Navigation, прокрутка | Прокрутка | 60 | 0,2 мс | 7% |
| Form, прокрутка | Прокрутка | 60 | 0,3 мс | 7% |
| Gestures | Пружины, пульсирующее кольцо | 60 | 0,3 мс | 3% |
| Animations | Нативные циклы | 60 | 0,7 мс | 6% |
| Canvas | Нативные циклы | 60 | 0,4 мс | 16% |
| Styles | Покачивающееся преобразование | 60 | 0,4 мс | 4% |
| Tailwind, прокрутка | Прокрутка | 60 | 0,2 мс | 7% |
| Text, прокрутка | Прокрутка | 60 | 0,1 мс | 7% |
| Text View, прокрутка | Прокрутка | 60 | 0,1 мс | 6% |
| Images, прокрутка | Прокрутка | 60 | 0,1 мс | 5% |
| Components, прокрутка | Прокрутка | 60 | 0,2 мс | 6% |
| Overlays, прокрутка | Прокрутка | 60 | 0,1 мс | 6% |
| Menus, прокрутка | Прокрутка | 60 | 0,2 мс | 7% |
| Controls, прокрутка | Прокрутка, переходы | 60 | 0,2 мс | 5% |
| Web and video, прокрутка | Прокрутка | 60 | 0,1 мс | 8% |
| System | `requestAnimationFrame`, таймеры | 60 | 0,3 мс | 4% |
| Standard APIs, прокрутка | Прокрутка | 60 | 0,1 мс | 7% |
| Storage, прокрутка | Прокрутка | 60 | 0,1 мс | 8% |

</div>

*FPS* — это частота, пока что-то движется, как и выше: при 60 Гц симулятора каждый кадр приходит через одно обновление после
предыдущего. Каждая страница, на которой ничего не движется, не рисует кадров и в покое использует 1–2% CPU. Процесс занимает 40 МБ на
домашней странице галереи и ~180 МБ после всего тура (намеренно кэшируемые изображения, документы страницы Text View).

На iPhone 17 Pro (ProMotion, 120 Гц), с хостом разработки и одним демо на экране, анимированные страницы
рисуют кадр при каждом обновлении:

<div class="data-table">

| Страница | Источник кадров | FPS | Кадр p95 | Дольше одного обновления | CPU | Память |
|---|---|---|---|---|---|---|
| Stocks | Живые данные, нативные циклы | 120 | 0,2–0,3 мс | 0% | 46–70% | 41–43 МБ |
| Canvas | Нативные циклы | 120 | 0,1 мс | 0% | 25–36% | 37–38 МБ |
| Animations | Нативные циклы | 120 | 0,2 мс | 0% | 3–11% | 37 МБ |

</div>

Stocks выполняет коммиты 20 раз в секунду (тик каждые 50 мс), каждый — за 1,5 мс в JS и 1,4 мс в ядре в потоке
движка (p95 — 2,2 и 2,0 мс), который занят 4–6% времени.

<a id="measuring-an-app"></a>

## Измерение приложения

Чтобы измерить приложение так же, запустите его тесты или шаги с помощью `--fps` и `-v` на `--target linux`, `macos`, `windows` или `ios`:

```sh
cargo build --release -p nsh-cli
python3 examples/gallery/tools/perf.py --target macos --only Stocks,Canvas   # the gallery, page by page
echo 'await app.wait(5000)' | nsh -v test App.tsx --target linux --fps        # any app
```
