Pull to refresh

Comments 45

Лучшее хранилище данных — DOM структура

Хм, вроде как везде рекомендуют лишний раз не лезть в DOM, так как это очень медленно.
Как раз недавно переписал одно небольшое приложение которое использовало «Лучшее хранилище данных — DOM структура». После того как хранилищем данных стали использоваться объекты — скорость увеличилась почти на порядок.
А чего вы пытаетесь достичь? Наибольшей скорости работы какой-то части скрипта или наибольшей скорости модификации кода, уменьшения затрат на тестирование и сокращения времени поиска ошибки? Лично я второе ставлю главнее первого.
Почему «или»? Я просто не буду использовать решение, которое заведомо значительно более медленное. И я как-то сильно сомневаюсь, что на этом можно выиграть значительное время при разработке. Уже ж не 2000-е годы, когда дебаг JS был по типу хождения по минному полю, сейчас отличные инструменты разработчика в браузерах.
Заведомо значительно более медленное насколько?
Супер, цифры радуют. 14 миллионов операций в секунду с DOM-деревом. 14 000 операций в милисекунду. Вы действительно считаете, что выборка из документа с 14 тысячами нод за 1 милисекунду, это очень медленно?

Не кажется, что это экономия на спичках?
Этот тест показывает цифры для страницы из одного элемента. В реальном проекте, где на странице 100500 элементов, все будет намного печальнее.
Реальный проект, 10 мегабайт данных на странице. Да, рендеринг самого браузера подтормаживает. Скорость работы SC — без изменений. Потому что зона видимости не 100500 нод, а в худшем случае сотня.

И кстати, не знаю как вы, но до сих пор вынужден при написании приложения оглядываться на IE, т.к. это корпоративный стандарт заказчика, а в IE все намного печальнее…
Именно из-за ИЕ6 в SC нет селекторов (querySelectorAll), есть только самые примитивные операции: выборка чайлдов да элементов по имени ноды (да этого, в принципе, и хватает) ИЕ куда больше проблем имеет с отображением большого количества информации (не касается последних версий, там все гораздо лучше), чем с производительностю DOM-выборок.
Ну вон выше же человек написал, разница почти на порядок. Вот тест к примеру
jsperf.com/property-access-dom-vs-object/3

если не использовать кэширование (в виде сохранения ссылки на объекты DOM), то получается в 20-100 раз медленнее, в зависимости от браузера.
Поддерживаю!
Кстати да — мой вариант еще более менее шустрый, т.к. поиск элемента в DOM производится только один раз. В реальном проекте это можно было бы назвать кэшем.

Более того, использование DOM чревато еще одной проблемой. В случае если количество элементов велико, то время манипуляций с элементами будет расти нелинейно.
Что бы точнее описать проблему, поясню на примере: допустим у вас есть 10 000 (100 500, 10^9...) элементов (товары, записи, письма, любые другие объекты, придумайте сами). Все объекты никогда не отображаются за один раз, допустим объекты отображаются по n% за один раз (это может быть постраничный просмотр, разделение на группы и т.п.). Если данные которые должна хранить модель вы храните в DOM, вам придется построить дерево из всех элементов, а затем манипулировать ими (скрывать или показывать нужные блоки) — это очень медленная операция, гораздо быстрее будет построить новый фрагмент из модели, и добавить его в дерево, предварительно очистив нужный узел (тут можно выбрать различную тактику, но суть я думаю ясна).

Таким образом, если вы храните данные только в DOM дереве, вы сами загоняете себя в угол.
Время манипуляций, но не выборки из дерева. Манипуляции всегда трудозатратны. Именно поэтому я практически не использую JS-темплейтирование в работе. И по этой же причине были придуманы зоны распространения событий — уменьшать количество перебираемых нод.


Объясните мне пожалуйста, где тут 20 в раз…
Вы не тот тест смотрели я говорил о jsperf.com/property-access-dom-vs-object/3
Сравнивать первый и третий вариант, второй это с сохранением ссылок на объекты DOM, что в случае с к примеру 1000 объектов, придется делать целый массив ссылок, из-за которого теряется смысл в хранении данных в DOM.
Когда к вам придут пользователи, и начнут плакать что у них все тормозит — расскажите им что вы ставите на первое место.
Знаете, никогда не приходили. Вернее жаловались немного на другое — на объемы данных. Само приложение не тормозит. Потому что объемы данных, которыми манипулирует фреймворк — ничтожны. Покажите мне форму, на которой будет визуально заметна задержка выборки данных из DOM-дерева. Реальную форму, а не рыбу с 10000 одинаковыми элементами.

Все бывает в первый раз — когда-нибудь придут.
Я не могу вам эту форму показать — придется поверить на слово. В одном закрытом проекте, одна страница включала ~ 1000 объектов, разбитых на группы. Между объектами было большое количество связей, типа, текущий объект зависит от состояния каких то других n объектов (у текущего объекта есть список id объектов от которых он зависит, с указанием зависимых состояний). У каждого объекта есть некоторое количество метрик, которые рассчитываются по различным группам объектов по различным алгоритмам (со сложным алгоритмом учета зависимостей, учета веса групп и т.п.).
На момент когда меня пригласили эту форму оптимизировать, данная форма в IE вызывала появление предупреждения «Сценарий на этой веб-странице замедляет работу Internet Explorer» (часто по 2-3 раза подряд). Полная переработка этой формы заняла несколько недель, попутно xml как формат передачи данных был заменен на json, все данные перенесены в модель, при отображении объектов пользователю из соответствующей части модели строится DocumentFragment, и вставляется в обертку формы после предварительной очистки обертки.

У меня не стояло цели измерить точную разницу в производительности, но по грубым оценкам, разница была примерно в 10 раз. Но самое главное, что после переработки тормоза и лаги пропали полностью, даже в IE.
Отличный пример! Спасибо.
Как это решаем мы. Никаких «голых данных», с сервера приходит готовый HTML. Я много раз писал, почему это дешевле, и не раз давал в пример twitter, который перешел с генерации контента на стороне клиента на генерацию на стороне сервера.

Связь между объектами в событийной модели вообще не нужна. Она возникает на момент распространения события конкретного типа. Например, если мы нажали на такой чекбокс, где-то должно что-то отключиться, где-то поменяться один блок на другой, где-то отмениться предыдущее состояние. Чекбокс имеет конктерное предназначение. Например, назовем его «У меня есть скидка».
Действия, которые будут выполнены в событийной модели: запустить событие "activate_discount", установить его значение в true.

Каждый элемент, который имеет обработчик события "activate_discount" сделает нужные ему действия. Один спрячется при значении события true, другой наоборот, снимет с себя класс ".hidden", третий пойдет восстанавливать предыдущее состояние, четвертый сгенерирует новое событие.

Связь между элементами и их состояниями возникает только на момент запуска события, поэтому смысла нет ее хранить в явном виде, обработчики своим присутствием ее создают.
Есть некоторые сомнения по поводу того что генерация html на стороне сервера дешевле — все же нужно учитывать что если мы используем 1 килобайт JSON, то из него может получиться HTML объемом на несколько порядков больше, а тут уже скорость передачи может сильно повлиять. Но спорить об этом бессмысленно, нужно оценивать конкретную ситуацию.

Ну а что до примера, приведенного мной — описанный функционал это часть большого и сложного приложения, которое должно работать как в online на рабочих станциях, так и в offline на планшетах и других устройствах. В случаем с планшетами источником данных служит не сервис а некое хранилище, которое особым образом синхронизируется с сервисом служащим источником данных для рабочих станций. Соответственно нет возможности использовать генерацию страницы на сервере, а объем передаваемых данных играет первостепенную роль. Это конечно некоторый частный случай, но я думаю он наглядно показывает, что очень многое нужно учитывать при выборе инструмента. Исходя из моего опыта (не только с этим примером) Ваш инструмент мне не кажется удобным.
Если интересно, можем обсудить тему дороговизны генерации контента в приватной беседе.

Синхронизировать можно данные в любом виде, генерировать их представление единоразово сразу после синхронизации. Да и и основная задача SC не в генерации контента, а в функциональных возможностях приложения. Вы можете продолжать генерировать контент любыми удобными способами, а вот работать потом с изменением его состояний — это как раз задача StateController'a. События — своеобразный транспорт. Доставляют нужные известия нужным потребителям. События приучают мыслить несколько асинхронно, не опираться на 100% на какие-то факты, и фактически меняют подходы к организации кода. Именно поэтому их очень плохо воспринимают. Они кажутся невероятно сложными, хотя реализация тупая, это видно по коду StateController'а.
Twitter вообще не показатель. Потому что, при нормальном разделении контента всё будет летать. А они как раз действовали вашими методами «та зачем экономить на спичках», наподключали мегабайт JS-кода, а потом внезапно оказалось, что браузер начинает тупить. Да и у Twitter своя хайлоадная специфика, так что на них ориентироваться бессмысленно.
Я не думаю, что разработчики твиттера сплошь и поголовно низкоквалифицированные специалисты. Если они посчитали более дешевым способом перенести генерацию контента на сторону сервера, значит на то были весомые причины. И проблема не в хайлоаде даже. Невозможно контроллировать нагрузку на клиенте.
Никто не говорит о квалификации, у них скорее подход ближе к вашему был. Главное, чтобы быстрее делалось, а не чтобы быстро работало. Да над ними же весь инет прикалывался, что для отображения 160 байтного твита нужно скачать мегабайт скриптов.
А если взять тот же gmail то они вполне прекрасно передают письма в виде JSON.
А какой мой подход? Не заниматься преждевременной оптимизацией? :)
Прежде чем говорить о проблемах, надо их сначала найти.

К слову, Gmail загружает пол мегабайта скриптов, и переходы из режима просмотра письма к списку идет с заметным подтормаживанием даже на производительном процессоре. Но это их мало беспокоит, ведь перед разработчиками стоит задача генерировать как можно меньше трафика.
В данном случае Gmail был примером разделения данных и HTML.
Каждый преследует свои собственные цели и ставит свои акценты. Если для меня важнее скорость внесения правок и изменений, потому что у меня нет лишних миллиарда долларов и монопольного положения на рынке, то я делаю все, чтобы не тратить время на поиски мифического идеального варианта. Понадобится перенести генерацию контента на сторону клиента — сделаю без зазрения совести. Понадобится избавиться от долгих выборок из DOM — внедрю кеширование.
Это похоже на сборник вредных советов :) Что бы ваше приложение сожрало батарейку как можно быстрее, что бы у вас было как можно больше шансов сделать утечки памяти, да и процессору нечего просто так простаивать.
Утечек нет. Им просто неоткуда браться. Количество действий, которые производятся, исчисляются не сотнями, а десятками. И процессору в большинстве случаев напрягаться особенно не надо.

Хотите предметно поговорить, давайте поговорим. Я могу развеять очень много ваших страхов.
Количество действий, которые производятся, исчисляются не сотнями, а десятками

А это вы что меряли и как? Откуда взялись сотни и десятки?

Вы же понимаете, все очень относительно. Пока приложение небольшое и легко все контролировать — то картина достаточно аккуратненькая. Когда приложение выростает до определенных размеров, то цепочки ивентов становиться трудно контролировать, появляются ситуации когда одно и то же действие выполняется по нескольку раз за одно действие. Когда вы оперируете большими объемами данных — то операции по вытаскиванию из дома становятся очень дорогостоящими (по процессору и по батарейке). Когда на одной странице большое количество модулей, взаимодействующих между собой, то подход «ноды как носители обработчиков» порождает утечки. Когда вы обнаруживаете, что надо избавлятся от jQuery, потому как он медленно, неоптимально работает и порой течет.

Вы неверно трактовали мой первый комментарий. Я не имел ввиду, что описанный вами подход в корне плохой. Я хотел сказать, что нет серебрянной пули и зачастую очень сложно утверждать, что одно однозначно плохо, а другое хорошо.
А это вы что меряли и как? Откуда взялись сотни и десятки?

Есть такое понятие, как зона видимости распространения события. Разработчики самостоятельно контроллируют эту зону, и количество нод, которые в нее попадают. Нет смысла отправлять событие всему документу.

Если вы одно и то же событие по нескольку раз отправляете одной ноде, то у вас явно проблемы с кодом. Разные события, конечно же, могут приходить. Я оперировал документами с 30000 нод. Никаких проблем с производительностью JS-части, и все потому, что для каждого события используется ограниченная зона видимости.

Утечек нет, так как нет ссылок на ноды (основная причина утечек). А нет ссылок потому, что они не нужны, данные собираются только по требованию из DOM'а, обрабатываются, и удаляются, и происходит это на момент запуска события. :)

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

Вы говорите с позиции своего опыта. Это очень хорошо, что у вас так все гладко легло. Я уверен что вы тщательно спроектировали все и оно работает как часы. Супер. Но я видел и другие ситуации, когда проект очень выростает. Мне интересно было быпродолжить эту беседу скажем через 5-7 лет активного роста и развития вашего проекта на такой архитектуре. Когда в вашей системе количество модулей измерялось бы сотнями, а количество строк кода десятками миллионами или десятками миллионов. (возможно у вас и тогда бы все ок работало и тогда было бы вдвойне интересно обсудить это).
30000 нод это не много. В большом проекте количество сущностей может исчеслятся сотнями тысяч. Если их хранить в нодах а не в памяти — то просто доступ к ним сожрет батарейку смартфона за пару часов.
Про события я наверное не совсем точно выразился. Я не говорю что каждое событие должно распространятся на весь документ. Я говорил про цепочку событий, когда одно событие порождает другое и так по цепочки, они волнообразно распространяются по различным модулям системы и иногда пересекаются. Т.е. одно события может инициировать цепочку из десятков событий далее.
Очень сложно продолжать конструктивный разговор, в формате комментариев :)
А что вы имеете в виду под сущностями, которые могут исчисляться сотнями тысяч?

Под сущностями я имею введу любого рода данные, какие-то беизне или технические объекты.
Приведите тогда пример приложения, которому единовременно требуется сотни тысяч данных?
Я бы не сказал что это просто приложение, скорее крупный SaaS сервис, или биллинг, или CRM система в крупной компании, или разного рода аналитика, система мониторинка чего-либо.
За спиной две информационно-аналитические системы. Никаких проблем не вижу. Все упирается в возможности браузера отображать (рендеринг) контент (обычный HTML+CSS, не JS-темплейтирование) В разработке биллингов тоже участвовал. Не увидел там особых сложностей. Да, форм много, табличек много, но это не повод для беспокойства.
Вести диалог в формате комментариев не очень удобно. Тем более в сложившийся ситуации, когда вы наотрез отказываетесь верить в то, что бывают приложения с большим колличеством данных :)
Почему вы считаете, что я не верю в приложения с большим количеством данных? Меня табличками в базе данных с миллиардом записей трудно удивить. Да и десятимегабайтными документами тоже. Я просто не понимаю ваших страшилок. Вы мне пытаетесь что-то донести, но я не могу понять что. Если есть примеры, давайте их разберем, а размышлять об каких-то абстракциях, которые могут быть напрочь оторваны от реальности, мне тоже не сильно улыбается. Я честно хочу разобраться, в чем проблема, что вас беспокоит. Это ведь весьма полезное занятие — проверять свое мироощущение и свои решения с помощью посторонних людей.

Примеры есть, но я к сожалению не могу их озвучить, поэтому приходится говорить на уровне абстракций.
Думаю вам нужна третья часть — измерение производительности и сравнение. Если у вас есть что сказать, говорите :)
Работаю над ней. Но появится она не раньше, чем на следующей неделе. Хочу еще, по совету, на github выложить все.
Sign up to leave a comment.

Articles