Эта страница переведена машинным способом и может содержать ошибки. Читать оригинал на английском

Документация

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

Сколько стоят приложения NSH, в измерениях. Как NSH обеспечивает их быстродействие и как измерить собственное приложение, описано в разделе Производительность.

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

По собственным замерам 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 Гц.

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

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

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

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

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

#Память

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

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

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

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

Приложение 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):

ПриложениеЯзыкРаннерАрхив
Hello world (--template rust)Rust16,5 МБ7,5 МБ
Hello world (--template ts)TSX, на QuickJS25,7 МБ11,6 МБ
Hello world (--template mixed)TSX и крейт Rust25,8 МБ11,7 МБ
sysmon 0.1.3Rust20,9 МБ, со снятыми символами9,7 МБ
Omasend 0.2.0Rust22,3 МБ9,8 МБ

Раннеры 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 (воспроизведение рисунков).

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

ДемоFPSFrame p95CPUGPU
Stocks: 22 живых спарклайна, внутридневной график, книга заявок, лента сделок1202,7–3,7 мс33–36%18–22%
Canvas: индикатор, часы, точечная матрица119–1200,6–0,7 мс4–7%0%
Animations: три нативных цикла, пружины, последовательности119–1200,3–1,6 мс5–8%0%

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

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

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

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

#Память

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

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

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

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

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

ПриложениеЯзыкИсполняемый файлВ zip .app
Hello world (--template rust)Rust12,4 МБ6,0 МБ
Hello world (--template ts)TSX на QuickJS19,8 МБ9,5 МБ
Hello world (--template mixed)TSX и крейт Rust19,8 МБ9,5 МБ
sysmon 0.1.3Rust21,6 МБ8,4 МБ (.tar.gz его релиза)
Omasend 0.2.0Rust18,4 МБ9,7 МБ (.tar.gz его релиза)

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

  • Кадры только при изменениях: 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 (запись и воспроизведение рисунков).

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

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

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

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

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

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

#Память

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

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

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

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

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

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

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

  • Кадры только при изменениях и рисуются только там, где изменилось: тикер, синхронизированный с композитором, работает, пока идёт нативная анимация, переход или 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-ячейки, привязанные значения).

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

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

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

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

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

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

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

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