В этом исследовании мы показываем, что даже если новый HTTP/2 протокол значительно улучшает скорость загрузки страницы, время для полного отказа от фронт-энд оптимизаций ещё не наступило. Сегодня мы сосредоточимся на спрайт-сетах.
HTTP/2 стал доступен в 2015, как альтернатива к замене многоуважаемого HTTP/1.1, используемого с 1997. Многие авторы предсказывают устаревание или, даже, контрпродуктивность фронт-энд оптимизаций. В список классических оптимизаций входят спрайты: группировка множества маленьких изображений (спрайтов) в одно большое (спрайт-сет).
Несмотря на быстрое внедрение поддержки и в браузерах и в серверах (вики, w3techs), мы не смогли найти опубликованные сравнительные замеры для подтверждения утверждения (прим. того, что спрайты больше не нужны). Как веб-архитекторы, тогда, мы естественно, интересовались, следует ли нам отказаться от подхода спрайтов или нет. Как гласит известная цитата Уильяма Эдвардса Деминга: “In God we trust, all others bring data”. Поэтому мы сделали свой собственный бенчмарк.
Первая часть этой статьи описывает основные отличия между HTTP/1.x и 2 и почему они могут способствовать устареванию спрайтов. Во второй части мы покажем сравнительные результаты бенчмарка.
Спрайт-сеты
Спрайт-сеты это фронт-энд оптимизация. Вместо загрузки нескольких изображений с сервера по одному (к примеру, набор иконок, которые используются по всей странице), один спрайт-сет загружается единожды и в дальнейшем, каждый спрайт может быть вырезан. Спрайт-сет используемый в нашем бенчмарке с сайта www.facebook.com показан на Рис. 1.
Рис. 1: спрайт-сет бенчмарка.
Этот спрайт-сет составлен из 72 спрайтов, которые могут быть точно вырезаны в маленькие индивидуальные изображения каждого спрайта.
Первое что бросается в глаза, это то, что спрайт-сет как единое глобальное изображение весит всего 71 kB, в то время как спрайты, как отдельные изображения, в сумме 106 kB, больше, почти на 40 %. Объединённый размер меньше чем сумма, благодаря лучшему сжатию изображения и уменьшению избыточных заголовков изображений. Более того, одного запроса на сервер достаточно для загрузки всех иконок сайта со спрайт-сетом, вместо множества запросов по одному на каждое изображение.
Для того чтоб отрисовать картинки в браузере, спрайт-сет нарезается на маленькие иконки в CSS коде. На Рис. 2 вы можете видеть HTML используемый с/без использования спрайтов, и соответствующие CSS стили. Результирующая нагрузка также отображена на графике.
CSS с отдельными изображениями | HTML (общий) | CSS со спрайт-сетом |
---|---|---|
|
|
|
Рис. 2: с/без использования спрайт-сетов.
Два изображения захвата HTTP запросов. Без спрайт-сета можно заметить несколько параллельных небольших запросов с суммарно большим временем завершения.
Очевидно, что CSS код значительно проще без спрайт-сета, однако время загрузки значительно короче с использованием спрайтов. При использовании спрайт-сетов возникает несколько технических накладок, они будут оговорены ниже.
Основные же ограничения подхода спрайт-сетов такие:
- более сложная разработка поскольку необходимо создавать или адаптировать спрайт-сет и поддерживать CSS код для разделения его на отдельные иконки. Несмотря на это, процесс может быть автоматизирован в разработке с инструментами типа Glue;
- обновление браузерного кеша при каждой модификации спрайт-сета, даже если это просто добавление, удаление или модификация одного спрайта.
HTTP/1.x и спрайты
В HTTP/1.x не более одного запроса (прим. одновременно) возможно в пределах одного TCP соединения между клиентом и сервером. Следующие запросы должны ждать в очереди для того, чтобы переиспользовать TCP соединение. В целях уменьшения времени загрузки и во избежание зависания страницы из-за одного длительного запроса, современные браузеры открывают несколько параллельных TCP соединений с сервером (как правило, от 2 до 8 соединений, в зависимости от браузера). Тем не менее, такое распараллеливание ограничено и использование большого количества запросов всё ещё значит что время загрузки будет больше, а сеть перегружена, даже не учитывая бэк-энд сторону.
Загрузка всех независимых изображений за раз приводит к множеству запросов в HTTP/1.x и сильно влияет на итоговое время загрузки, отсюда и на UX, в то время как использование спрайт-сета в результате приведёт к одному запросу что является огромной оптимизацией на многих веб-сайтах.
HTTP/2 и спрайты
С HTTP/2 все запросы между браузером и сервером мультиплексированы (прим. уплотнены) в одно TCP соединение.
Компенсируя стоимость открытия и закрытия множества соединений это приводит к лучшему использованию TCP соединения и ограничивает влияние на задержки между клиентом и сервером.
Это позволяет загружать десятки изображений в параллели в одном TCP соединении. Как следствие, использование спрайт-сетов для уменьшения количества запросов может стать бесполезным. Все эти предложения (прим. фразы) пока являются гипотезами: эта статья покажет как реальность отличается от теории.
Методология бенчмарка
Весь код для повторения этого бенчмарка доступен на Github.
Для воспроизведения различных ситуаций, были созданы шесть HTML страниц. Первая из них использует спрайт-сет, остальные же, включают в себя различные количества отдельных изображений.
Имя настройки | Изображения | Кол-во |
---|---|---|
Single | спрайт-сет | 100% (72) |
AllSplitted | отдельная | 100% |
80pSplitted | отдельная | 80% |
50pSplitted | отдельная | 50% |
30pSplitted | отдельная | 30% |
10pSplitted | отдельная | 10% |
Последние четыре страницы, только с частью спрайтов, показывают наиболее распространённую ситуацию, когда все спрайты не используются одновременно: необходима только часть изображений, в зависимости от контекста (язык, геолокация, состояние приложения и т.п.). Использование отдельных изображений вместо спрайт-сетов позволяет загружать только те из них, которые нужны из всей коллекции. Однако результаты продемонстрируют как группировка остаётся эффективной.
В нашем бенчмарке был разработан JavaScript код для вычисления промежутка времени между окончанием загрузки HTML страницы (исполнения скриптов в заголовке страницы) и загрузкой последнего изображения (последнего события onload
). Этот промежуток времени рассчитан и записан для каждого случая. Страницы и изображения были загружены на два сервера NGINX 1.9.5 расположенные в одном датацентре на двух идентичных виртуальных машинах.
Один сервер поддерживает HTTP/2, в то время как другой поддерживает только HTTP/1.1. Страницы запрашиваются через HTTPS, даже с HTTP/1.1, для того, чтобы сравнение проходило максимально честно по отношению к HTTP/2 который поддерживает только защищённую передачу.
На стороне клиента был разработан Python скрипт для запроса страниц через два браузера, Firefox 41.0 и Chrome 45.0 (прим. актуальные версии на момент написания статьи), запускаемые через Selenium WebDriver. Selenium позволяет иметь новый контекст в браузере для каждого запроса для избежания эффекта кеширования. Конечно, если изображения кешировались бы браузером, мы не смогли бы протестировать протокол поскольку там не было бы реальной передачи (только пустое тело с кодом 304). Selenium также позволяет просто проверять DOM для получения временного промежутка рассчитанного JavaScript'ом и выведенного на странице.
Рис. 3: архитектура кода тестирования.
В целях получения надёжных результатов, протокол запускается сто раз для каждого случая, как показано в псевдокоде ниже.
for i = 1 to 100
for page in ('Single', 'AllSplitted', '80pSplitted',
'50pSplitted', '30pSplitted', '10pSplitted')
for protocol in ('HTTP/1.1', 'HTTP/2')
for browser in ('Firefox', 'Chrome')
#load page and measure load time
Для каждого случая записывается значение медианы. Если посмотреть на распределение времени для одного случая (см. Рис. 4), мы наблюдаем резко выделяющиеся значения, обусловленные изначально стохастическим (прим. случайным) процессом сети. Влияние этих точек на среднее значение будет велико. С другой стороны медиана является надёжным индикатором поскольку распределение практически однородное.
Рис. 4: исходные данные времени загрузки, при повторении вычисления сто раз.
Для расширения области актуальных ситуаций, протокол был повторён на трёх конфигурациях клиента:
конфигурация | описание | средняя задержка | пропусканая способность загрузки |
---|---|---|---|
#1 | ВМ в хорошем датацентре | 10ms | 80Mb/s |
#2 | ноутбук с хорошим интернет соединением | 40ms | 20Mb/s |
#3 | ноутбук с плохим интернет соединением | 35ms | 1.3Mb/s |
Результаты бенчмарка
Три конфигурации дали очень когерентные (прим. логически последованные) результаты, показанные на Рис. 5.
Рис. 5: суммарные медианы загрузки для разных типов страниц, браузеров, http протоколов и конфигураций сети.
По графикам видно, что:
- время загрузки спрайт-сета равно или меньше, чем 10% отдельных изображений, даже при соединении с низкой задержкой. На других конфигурациях спрайт-сет значительно быстрее загружается чем индивидуальные спрайты, вне зависимости от используемого HTTP протокола;
- HTTP/2 приносит очевидное уменьшение времени загрузки в сравнении с HTTP/1.1, но улучшение HTTP протокола не является достаточным для уменьшения полезности фронт-энд оптимизаций;
- при текущих замерах браузер незначительно влияет на разницу (разница времени загрузки на конфигурации #1 возможно вызвана ограничением CPU и памяти на виртуальной машине).
Для дальнейшего анализа этих результатов можно также отобразить на графике медианное время загрузки в соотношении к кол-ву запросов или сумме размеров изображений. Рис. 6 показывает результаты для вышеописанной конфигурации #3.
Рис. 6: те же эксперименты, как и на Рис. 5, с конфигурацией #3, но сравнивая зависимость времени к кол-ву изображений и сумме размеров изображений.
На основании этих двух графиков можно судить о том, что подход спрайт-сетов сильно отличается от отдельных спрайтов из-за использования одного запроса, в то время как суммарный размер выступает вторым по влиянию на скорость.
Заключение
Этот бенчмарк явно отстаивает что оптимизация спрайт-сетами всё ещё актуальна, даже после апгрейда на протокол HTTP/2. Даже не смотря на то, что новый протокол предлагает значительное улучшение времени загрузки (до 50%) в сравнении с HTTP/1.1, этого может быть недостаточно. Если HTTP/2 оптимизирует использование сети, это не станет основанием для полного отказа от фронт-энд оптимизаций, среди которых спрайт-сеты, CSS/JS минификации и бандлы.
Прим. переводчика: в статье есть некоторые устаревшие данные обусловленные датой выхода статьи (декабрь 2015 года), однако основной посыл остаётся актуальным.
Автор: extempl