Не так давно на конференции ChainReact 2019 Facebook официально запустил движок исполнения JavaScript нового поколения Hermes. Hermes — это небольшой и легкий движок JavaScript, оптимизированный для запуска React Native на Android. Для многих приложений простое включение Hermes может улучшить время запуска, использование памяти и размер приложения, а также, поскольку он реализован в стандартах JavaScript, его легко интегрировать в приложения React Native.
О Гермесе
С момента запуска ReactNative было подключено и использовано большое количество приложений, включая основной бизнес-процесс крупномасштабных приложений. Поскольку сложность бизнеса продолжает расти, проблемы с производительностью становится невозможно игнорировать.
Анализируя данные о производительности, команда Facebook обнаружила, что движок JavaScript является важным фактором, влияющим на производительность запуска и размер пакета приложения. Поскольку JavaScriptCore изначально был разработан для настольных браузеров, по сравнению с настольными компьютерами, возможности мобильных устройств имеют слишком много ограничений.Чтобы оптимизировать производительность мобильных устройств снизу, команда Facebook решила создать свой собственный движок JavaScript и разработала Hermes, который ограничено ограничениями обзора iOS AppStore, в настоящее время только для платформы Android.
Набор тестовых данных двигателя Hermes был официально предоставлен на конференции Chain React:
- Время от запуска страницы до действия пользователя (время до взаимодействия: TTI) сокращено с 4,3 до 2,01 с.
- Размер загружаемого приложения уменьшен с 41 МБ до 22 МБ.
- Объем памяти уменьшен со 185 МБ до 136 МБ.
Можно обнаружить, что после перехода на Hermes значительно улучшились три ключевых показателя: время загрузки, размер приложения и занятость памяти.
Поскольку Hermes оптимизирован для мобильных приложений, мы не планируем интегрировать его в какой-либо браузер или серверную инфраструктуру, например Node.js. Существующие механизмы JavaScript по-прежнему предпочтительны в этих средах.Решение Hermes для оптимизации
При разработке мобильных приложений первая загрузка и запуск, объем памяти и размер приложения являются важными показателями для измерения качества приложения, поэтому Hermes также оптимизирует приложения React Native с учетом этих аспектов.
предварительная компиляция байт-кода
Вообще говоря, механизм JavaScript будет анализировать исходный код JavaScript и генерировать байт-код после его загрузки, а код JavaScript необходимо выполнить после создания байт-кода. Чтобы пропустить этот шаг, Hermes представляет прекомпилятор, который запускается в процессе сборки мобильного приложения. Это дает больше времени для оптимизации байт-кода, делая байт-код меньше и эффективнее. Теперь также можно оптимизировать всю программу, например, выполнить дедупликацию и упаковать таблицы строк.
Байт-код разработан таким образом, что его можно отображать в память и интерпретировать во время выполнения без жадного чтения всего файла. Плохая производительность операций ввода-вывода во флэш-памяти на многих мобильных устройствах низкого и среднего класса значительно увеличивает задержку, поэтому загрузка байт-кода, оптимизированного для объема, из флэш-памяти по запросу может значительно улучшить TTI. Кроме того, поскольку память отображается только для чтения и поддерживается файлами, мобильные операционные системы, не использующие виртуальную память (например, Android), могут очищать эти страницы при нехватке памяти, уменьшая число остановок процессов на устройствах с меньшим объемом памяти.
Хотя сжатый байт-код немного больше, чем сжатый исходный код JavaScript, Hermes уменьшает общий размер Android-приложения проекта React Native из-за меньшего размера собственного кода Hermes.Нет JIT
Чтобы увеличить скорость выполнения, популярный движок JavaScript может компилировать часто интерпретируемый код как машинный код, который выполняется мгновенным (JIT) компилятором.
В настоящее время у Hermes нет JIT-компилятора. Это означает, что Hermes не будет очень хорошо работать в некоторых тестах, особенно в тех, которые полагаются на производительность процессора. Это сделано специально: эти бенчмарки едва ли отражают реальную нагрузку мобильных приложений. Мы также провели несколько экспериментов с JIT, но считаем важным сосредоточиться на приведенных выше реальных показателях, если вы хотите получить реальный прирост скорости. Поскольку JIT должны прогреваться при запуске приложения, они вряд ли улучшат TTI и даже могут повредить TTI. Кроме того, JIT увеличивает размер собственного кода и потребление памяти, что негативно влияет на наши основные показатели. JIT может тормозить важные для нас показатели, поэтому мы решили не внедрять JIT.
Политика сбора мусора
Эффективное использование памяти в мобильных устройствах очень важно. Вообще говоря, недорогие устройства, как правило, имеют ограниченный объем памяти, поэтому операционная система принудительно убивает приложения, использующие слишком много памяти. Когда приложение закрывается и используется снова, его необходимо медленно перезапускать, и это также влияет на фоновые функции. В ходе раннего тестирования мы узнали, что пространство виртуальных адресов (VA), особенно непрерывное пространство VA, может быть конечным ресурсом при запуске больших приложений на 32-разрядных устройствах, и что отложенное выделение физических страниц мало помогает.
Поэтому, чтобы максимально оптимизировать память и пространство VA, используемое движком, мы построили сборщик мусора со следующими функциями.Основные меры:
- Выделение по требованию: выделяйте пространство виртуального устройства блоками только при необходимости.
- Несмежные: пространство виртуальных машин не обязательно должно находиться в пределах одного диапазона памяти, что позволяет избежать ограничений ресурсов на 32-разрядных устройствах.
- Перемещение: возможность перемещения объекта означает фрагментацию памяти и возврат блока, который больше не нужен операционной системе.
- Generational: не сканируйте всю кучу JavaScript при каждой сборке мусора, что сокращает время сборки мусора.
Интеграция Гермеса
Быстро начните работу с Hermes
Команда Faceback загрузила инструмент Hermes в npm:hermesvm. Инструмент hermes может напрямую запускать JS-код, преобразовывать байт-код и предоставлять множество параметров для управления настройкой.
Например, ниже показано, как hermesvm выполняет код JS и преобразует функцию байт-кода, код выглядит следующим образом:
// 创建hermes_test文件,内容:print("This is Hermes Demo");
vim hermes_test.js
// 直接执行纯文本js
~/node_modules/hermesvm/osx-bin/hermes hermes_test.js
This is Hermes Demo
// 转换成bytecode
~/node_modules/hermesvm/osx-bin/hermes --emit-binary hermes_test.js -out hermes_test.hbc
// 执行字节码
~/node_modules/hermesvm/osx-bin/hermes hermes_test.hbc
This is Hermes Demo
Интеграция в новый проект
目前 Hermes 是一个可选的 React Native 功能
. Если вы хотите включить Hermes, вам необходимо убедиться, что версия вашего проекта React Native выше 0.60.2, а также вам необходимо внести следующие изменения в файл android/app/build.gradle.
project.ext.react = [
entryFile: "index.js",
enableHermes: true
]
Если приложение было создано хотя бы один раз, используйте следующую команду для его очистки.
cd android && ./gradlew clean
Затем вы можете разработать и развернуть свое приложение как обычно.
react-native run-android
отладка
Чтобы обеспечить удобство отладки, мы реализовали поддержку удаленной отладки Chrome через протокол DevTools. На сегодняшний день React Native поддерживает только отладку с помощью прокси-сервера в приложении при запуске кода JavaScript вашего приложения в Chrome. С этой поддержкой можно отлаживать приложение, но синхронные собственные вызовы недоступны в мосте React Native. Поддержка Hermes протокола удаленной отладки позволяет разработчикам подключаться к движку Hermes, работающему на их устройстве, и отлаживать свои приложения, используя тот же движок, что и в рабочей среде. Помимо отладки, мы также рассматриваем возможность реализации дополнительной поддержки протокола Chrome DevTools.
Сравнение Hermes, JavaScriptCore и V8
После официальной проверки данных ключевые показатели, предложенные командой Faceback, были значительно улучшены по сравнению с исходным решением JavaScriptCore.
Во-первых, по размеру нативного so-файла, необходимой so-библиотеки, на которую опирается RN, Hermes примерно на 16% меньше, чем JavaScriptCore (одна архитектура armeabi сжимается примерно на 0,5M), а V8 намного больше чем Hermes и JavaScriptCore.
Далее идет флуктуация памяти, возьмемRNTesterПосле входа на страницу RN и перехода на несколько страниц и выхода из инженерного теста можно увидеть колебания памяти, и рост памяти V8 и Hermes должен быть более плавным.Далее идут колебания ЦП.Пройдите тест проекта RNTester и войдите на страницу RN, чтобы перейти на несколько страниц и выйти, и сравните колебания ЦП. Hermes значительно лучше, чем V8 и JavaScriptCore. ,Как показано ниже.Проблемы с Гермесом
По сравнению с JavaScriptCore у Hermes действительно есть много преимуществ, но он не обязательно лучше, чем JavaScriptCore.По мере тестирования и интеграции постепенно появляются проблемы, привносимые Hermes.
Размер файла байт-кода слишком велик
В результате теста файл байт-кода, скомпилированный Hermes, на 100% больше, чем простой текстовый файл js. Следовательно, выходной пакет RN будет относительно большим, и когда инкрементный пакет RN выдается динамически, эффективность различия также будет снижена из-за различий в двоичном файле.
Чтобы решить эту проблему, в соответствии с характеристиками Hermes, мы изменили свое мышление и поставили компиляцию байт-кода Hermes на стороне клиента.На стороне клиента хранятся как файлы js, так и файлы байт-кода.Если компиляция байт-кода завершена, используйте Hermes, в противном случае по-прежнему используйте JavaScriptCore.
Проект с открытым исходным кодом Hermes предоставляет метод complieJS для компиляции байт-кода, но эта часть кода по умолчанию не упакована в движок Hermes RN.Мы немного интегрируем и упаковываем его и предоставляем через JNI для использования в бизнесе.
Выполнение обычного текста js занимает много времени
Мы позволяем Hermes загружать обычный текст перед преобразованием простого текста js в байт-код на стороне клиента. Но фактический тест показал, что производительность Hermes при загрузке обычного текста почти на 30% ниже, чем у JavaScriptCore. Основная причина заключается в том, что Hermes удаляет JIT-функцию, из-за чего простой текстовый js-код работает медленнее.
Непрерывная оптимизация
Чтобы упростить миграцию Hermes и продолжить поддержку JavaScriptCore на iOS, мы создали JSI — упрощенный API для встраивания движков JavaScript в приложения C++. Этот API позволяет инженерам React Native внедрять собственные улучшения инфраструктуры. Fabric использует JSI, который может опережать рендеринг React Native; TurboModules также использует JSI, который уменьшает размер собственных модулей и может лениво загружаться в соответствии с потребностями приложений React Native.
Ссылки по теме: