Реагировать очень быстро, но для того, чтобы поддерживать хороший темп, ему нужна помощь в понимании того, какие части представления следует или не следует обновлять. Эту проблему, какие компоненты представления необходимо обновить с учетом некоторых изменений в модели, можно решить с помощью различных стратегий .

React предлагает особый подход: использование постоянных структур данных.

Неизменяемость делает отслеживание изменений дешевым; изменение всегда приводит к созданию нового объекта, поэтому нам нужно только проверить, изменилась ли ссылка на объект.

Введите Immutable-JS

React рекомендует использовать библиотеку, также разработанную Facebook, immutable-js. Он черпает вдохновение из реализаций постоянных структур данных, которые уже были протестированы в производственной среде на языках функционального программирования, таких как Scala или Haskell.

Эти реализации основаны на структуре данных, называемой HAT-trie, которая позволяет структурное разделение и сводит к минимуму необходимость копирования данных.

Осторожно: впереди лежачие полицейские!

Изначально мы были довольны работой дуэта React и immutable-js. Однако вскоре мы сталкиваемся с вариантом использования, который делает очевидным отсутствие производительности при изменении модели: выполнение большого количества небольших изменений над очень небольшими записями.

Записи имеют фиксированный известный набор атрибутов. Количество атрибутов в записи обычно невелико. По нашему опыту, более 90% определений классов записей имеют менее 20 атрибутов, а более 90% использований записей имеют менее 35 атрибутов.

В immutable-js реализация записей в настоящее время основана на реализации Maps, с дополнительным битом ограничения на поля, которые вы можете использовать. Эта реализация хороша для коллекции "ключ-значение" произвольного размера, такой как обычная карта. Но для записей с фиксированными атрибутами, обычно с небольшим количеством атрибутов, ли дополнительная сложность попыток HAT превосходит копирование всех данных?

Копирование всех данных может показаться глупым. Похоже на отказ от преимущества структурного разделения: копирование меньшего количества данных, что означает использование меньше памяти и тратить меньше времени на копирование. Но для небольшого количества атрибутов память, необходимая для копирования всех атрибутов, будет сопоставима с памятью, используемой новыми структурами дерева. Кроме того, процесс копирования, вероятно, будет более эффективным, поскольку он не потребует навигации по структуре дерева.

Постоянная структура, для которой требуется полная копия измененной версии, не является новой концепцией, и она также присутствует во многих языках функционального программирования, принимая такие имена, как Record или Named Tuple .

В поисках лучшей записи

После нескольких итераций настройки, которые будут подробно описаны в следующей статье, мы создали фабричную функцию, которая, предоставив набор значений по умолчанию, динамически создает конструктор объектов Record с помощью get и set методы. Это очень похоже на immutable-js, что позволило нам использовать его как заменяющую замену для Immutable.Record в нашем коде.

Код нашей реализации доступен на GitHub.

Использование нашей версии Records дало хорошие улучшения производительности в сценарии использования, который отправил нас на этот квест. После проверки выигрыша в реальном сценарии нам все еще было любопытно, сколько атрибутов потребуется записи, чтобы дополнительная сложность HAT-trie превзошла полную копию. Итак, мы создали небольшой тест и запустили его в node.js.

time%           attributes
--------------------------
5.5%            5  
6.6%            10 
8.8%            15 
15.3%           20 
20.9%           25 
22.9%           30 
23.7%           35 
25.9%           40 
32%             45 
32.7%           50 
36.8%           60 
43.4%           70 
49.3%           80 
66.1%           90 
66.9%           100
75.8%           120
225.5%          140

В этом тестовом примере для операций set с записями с менее чем 50 атрибутами наша реализация работала примерно в 3 раза быстрее, чем у Facebook. В записях с менее чем 20 атрибутами он выполняется в 15% случаев - это примерно в 6 раз быстрее, чем в исходной реализации! Операции Get были стабильно быстрее, несмотря на количество атрибутов записи. Добавление их к эталону всегда будет приводить к смещению цифр в пользу нашей реализации. Immutable-js снова становится лидером, когда номер атрибута становится достаточно высоким, чтобы заставить V8 отказаться от оптимизации объекта, который мы используем для переноса данных.

Быстро доставить

Имеет смысл стоять на плечах гигантов, и использование React и immutable-js - хороший выбор. Мы по-прежнему считаем Immutable-js отличной библиотекой для большинства случаев использования.

Однако не помешает хорошо знать свои варианты использования и то, как они соответствуют алгоритмам и структурам данных, которые вы будете использовать - иногда могут быть лучшие альтернативы.

Анализ производительности и настройка приложений и библиотек JavaScript - это тяжелая работа, но она позволила нам создавать гораздо более быстрые приложения.

И это оставило нам несколько историй для будущих публикаций.

Дальнейшее чтение:

Идеи, изложенные в этом посте, частично являются результатом исследовательского проекта RADicalize, который был поддержан ЕС и PT2020.