Опубликован: 16.02.2009 | Доступ: свободный | Студентов: 1429 / 138 | Оценка: 4.26 / 4.17 | Длительность: 16:10:00
ISBN: 978-5-9963-0024-2
Лекция 5:

Параллельные соединения

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >

Влияние заголовков

Давайте теперь посмотрим, как размер заголовков влияет на эффективную пропускную способность канала. Предыдущий график предполагает, что размер заголовков составляет 500 байтов дополнительно к размеру объекта, как для запроса, так и для ответа. Как же изменение этого параметра отразится на производительности нашего 1,5Мб / 384Кб канала и расстояния до пользователя в 100мс? Предполагается, что пользователь уже изначально использует 4 хоста и активное соединение.

 Влияние заголовков на эффективную пропускную способность канала. Источник:www.die.net

Рис. 5.6. Влияние заголовков на эффективную пропускную способность канала. Источник:www.die.net

На графике хорошо видно, что при небольших размерах файлов основные задержки приходятся на исходящий канал. Браузер, отправляющий "тяжелые" запросы на сервер (например, с большим количеством cookie), по-видимому, замедляет скорость передачи данных более чем на 40% для этого пользователя. Естественно, размер cookie можно и нужно регулировать на сервере. Отсюда простой вывод: cookie нужно по возможности делать минимальными или направлять ресурсные запросы на серверы, которые не выставляют cookie.

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

Уплотняем поток загрузки

Рассмотрев методы сжатия, объединения, кэширования и создания параллельных соединений, разумно было бы озадачиться следующим вопросом: какая часть страницы должна загружаться вместе с основным HTML-файлом, а какая — только с внешними файлами?

Было собрано тестовое окружение в виде одной страницы, для которой применены различные оптимизационные техники (заодно было получено реальное ускорение для загрузки произвольной страницы и показано, как все эти техники реально влияют на скорость загрузки страницы).

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

Реальная ситуация

 Диаграмма загрузки (неизмененного) сайта WebHiTech.ru

Рис. 5.7. Диаграмма загрузки (неизмененного) сайта WebHiTech.ru

Основная идея вариации потока загрузки заключалась в создании минимального количества "белых мест" на диаграмме загрузки. Как видно из рис. 29, около 80% при загрузке страницы составляют простои соединений (естественно, что данный график не отражает реальную загрузку открытых в браузере каналов загрузки, однако при уточнении картины ситуация практически не меняется). Параллельные загрузки начинаются только после прохождения "узкого места", которое заканчивается (в данном случае) после предзагрузки страницы — после CSS-файла.

Для оптимизации скорости загрузки нам нужно уменьшить число файлов (вертикальные стрелки), загружающихся параллельно, и "сдвинуть" их максимально влево (горизонтальная стрелка). Уменьшение "белых мест" (фактически, уменьшение простоя каналов загрузки), по идее, должно увеличить скорость загрузки за счет ее распараллеливания. Давайте посмотрим, действительно ли это так и как этого добиться.

Шаг первый: простая страница

Вначале бралась обычная страница, для которой использовалось только gzip -сжатие HTML-файла. Это самое простое, что может быть сделано для ускорения загрузки страницы. Данная оптимизация бралась за основу, с которой сравнивалось все остальное. Для тестов препарировалась главная страница конкурса WebHiTech (http://webhitech.ru/) с небольшим количеством дополнительных картинок (чтобы было больше внешних объектов, и размер страницы увеличивался).

В самом начале ( head ) страницы замеряется начальное время, а по событию window.onload (заметим, что только по нему, ибо только оно гарантирует, что вся страница целиком находится в клиентском браузере) — конечное, затем вычисляется разница. Но это очень простой пример, перейдем к следующим шагам.

Шаг второй: уменьшаем изображения

Для начала минимизируем все исходные изображения (основные прикладные техники уже были освещены во второй главе). Получилось довольно забавно: суммарный размер страницы уменьшился на 8%, и скорость загрузки возросла на 8% (т. е. получилось пропорциональное ускорение).

Дополнительно с минимизацией картинок была уменьшена таблица стилей (через CSS Tidy) и сам HTML-файл (убраны лишние пробелы и переводы строк). Скриптов на странице не было, поэтому общее время загрузки изменилось несильно. Но это еще не конец, и мы переходим к третьему шагу.

Шаг третий: все-в-одном

Можно использовать data:URI и внедрить все изображения в соответствующие HTML/CSS-файлы, уменьшив таким образом размер страницы (за счет gzip -сжатия, по большому счету, потому что таблица стилей перед этим не сжималась) еще на 15%, однако время загрузки при этом уменьшилось всего на 4% (при включенном кэшировании уменьшилось число запросов с 304-ответом). При загрузке страницы в первый раз улучшения гораздо более стабильны: 20%.

CSS-файл, естественно, тоже был включен в HTML, поэтому при загрузке всей страницы осуществлялся только один запрос к серверу (для отображения целой страницы с парой десятков объектов).

Шаг четвертый: нарезаем поток

Можно попробовать распределить первоначальный монолитный файл на несколько (5-10) равных частей, которые бы затем собирались и внедрялись прямо в document.body.innerHTML. Т. е. сам начальный HTML-файл очень мал (фактически, содержит только предзагрузчик) и загружается весьма быстро, а после этого стартует параллельная загрузка еще множества одинаковых файлов, которые используют канал загрузки максимально плотно.

Однако, как показали исследования, издержки на XHR-запросы и сборку innerHTML на клиенте сильно превосходят выигрыш от такого распараллеливания. В итоге страница будет загружаться в 2-5 раз дольше, размер при этом изменяется несильно.

Можно попробовать использовать вместо XHR-запросов классические iframe, чтобы избежать части издержек. Это помогает, но не особенно. Страница все равно будет загружаться в 2-3 раза дольше, чем хотелось бы.

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

Также стоит упомянуть, что при использовании iframe для навигации по сайту встает проблема обновления этой самой навигации (например, если мы хотим выделить какой-то пункт меню как активный). Корректное решение этой проблемы требует от пользователя включенного JavaScript, и оно довольно нетривиально с технической стороны. В общем, если при проектировании сайта без фреймов можно обойтись — значит их не нужно использовать.

Шаг пятый: алгоритмическое кэширование

Проанализировав ситуацию с первыми тремя шагами, мы видим, что часть ускорения может быть достигнута, если предоставить браузеру возможность самому загружать внешние файлы как отдельные объекты, а не как JSON-код, который нужно как-то преобразовать. Дополнительно к этому всплывают аспекты кэширования: ведь быстрее загрузить половину страницы, а для второй половины проверить запросами со статус-кодами 304, что объекты не изменились. Загрузка всей страницы клиентом в первый раз в данном случае будет немного медленнее (естественно, решение по этому поводу будет зависеть от числа постоянных пользователей ресурса).

В результате удалось уменьшить время загрузки еще на 5%, итоговое ускорение (в случае полного кэша) достигло 20%, размер страницы при этом уменьшился на 21%. Возможно вынесение не более 50% от размера страницы в загрузку внешних объектов, при этом объекты должны быть примерно равного размера (расхождение не более 20%). В таком случае скорость загрузки страницы для пользователей с полным кэшем будет наибольшей. Если страница оптимизируется под пользователей с пустым кэшем, то наилучший результат достигается только при включении всех внешних файлов в исходный HTML.

Итоговая таблица

Ниже приведены все результаты оптимизации для отдельной взятой страницы. Загрузка тестировалась на соединении 100 Кб/с, общее число первоначальных объектов: 23.

Таблица 5.1. Различные способы параллельной загрузки объектов на странице
Номер шага Описание Общий размер (кб) Время загрузки (мс)
1 Обычная страница. Ничего не сжато (только html отдается через gzip ) 63 117
2 HTML/CSS файлы и картинки минимизированы 58 108
3 Один-единственный файл. Картинки вставлены через data:URI 49 104
4 HTML-файл параллельно загружает 6 частей с данными и собирает их на клиенте 49 233
4.5 HTML-файл загружает 4 iframe 49 205
5 Вариант № 3, только JPEG-изображения (примерно одинаковые по размеру) вынесены в файлы и загружаются через ( new Image ()). src в head - странице 49 98

Шаг шестой: балансируем стадии загрузки

Итак, как нам лучше всего балансировать загрузку страницы между ее стадиями? Где та "золотая середина", обеспечивающая оптимум загрузки? Начнем с предположения, что у нас уже выполнены все советы по уменьшению объема данных. Это можно сделать всегда, это достаточно просто (в большинстве случаев нужны лишь небольшие изменения в конфигурации сервера). Также предположим, что статика отдается уже с кэширующими заголовками (чтобы возвращать 304-ответы в том случае, если ресурсный файл физически не изменился с момента последнего посещения).

Что дальше? Дальнейшие действия зависят от структуры внешних файлов. При большом (больше двух) числе файлов, подключаемых в head страницы, необходимо объединить файлы стилей и файлы скриптов. Ускорение предзагрузки страницы будет налицо.

Если объем скриптов даже после сжатия достаточно велик (больше 10 Кб), то стоит их подключить перед закрывающим </body>, либо вообще загружать по комбинированному событию window.onload (динамической загрузке скриптов посвящено начало седьмой главы). Тут мы фактически переносим часть загрузки из второй стадии в четвертую — ускоряется лишь "визуальная" загрузка страницы.

Общее количество картинок должно быть минимальным. Однако тут тоже очень важно равномерно распределить их объем по третьей стадии загрузки. Довольно часто одно изображение в 50-100 Кб тормозит завершение загрузки — разбиение его на 3-4 составляющие способно ускорить общий процесс. Поэтому при использовании большого количества фоновых изображений лучше разбивать их на блоки по 10–20, которые будут загружаться параллельно.

Шаг седьмой: балансируем кэширование

Если все же на странице присутствует больше 10 внешних объектов в третьей стадии (картинок и различных мультимедийных файлов), тут уже стоит вводить дополнительный хост для увеличения числа параллельных потоков. В этом случае издержки на DNS-запрос окупятся снижением среднего времени установления соединения. 3 хоста стоит вводить уже после 20 объектов, и т. д. Всего не более 4 (как показало исследование рабочей группы Yahoo!, после 4 хостов издержки скорее возрастут, чем снизятся).

Вопрос о том, сколько объема страницы включать в сам HTML-файл (кода в виде CSS, JavaScript или data:URI ), а сколько оставлять на внешних объектах, решается очень просто. Баланс в данном случае примерно равен соотношению числа постоянных и единовременных посещений. Например, если 70% пользователей заходят на сайт в течение недели, то примерно 70% страницы должно находиться во внешних объектах и только 30% — в HTML-документе.

Когда страницу должны увидеть только один раз, логично будет включить все в саму страницу. Однако тут уже вступают в силу психологические моменты. Если у среднего пользователя страница при этом будет загружаться больше 3-4 секунд (учитывая время на DNS-запрос и соединение с сервером), то будет необходимо разбиение на две части: первоначальная версия, которая отобразится достаточно быстро, и остальная часть страницы.

Очень важно понимать, какая стадия загрузки при этом оптимизируется и что видит реальный пользователь (с чистым кэшем и, может быть, небыстрым каналом). Подробнее об анализе процесса загрузки страницы на конкретных примерах рассказывается в восьмой главе.

Заключение

Вот так, на примере обычной страницы (уже достаточно хорошо сделанной, стоит отметить) мы добились ускорения ее загрузки еще на 15-20% (и это без учета применения gzip -сжатия для HTML, которое в данном случае дает примерно 10% от общей скорости). Наиболее важные методы уже приведены выше, сейчас лишь можно упомянуть, что при оптимизации скорости работы страницы лучше всегда полагаться на внутренние механизмы браузера, а не пытаться их эмулировать на JavaScript (в данном случае речь идет об искусственной "нарезке" потока). Может быть, в будущем клиентские машины станут достаточно мощными (или же JavaScript-движки — лучше оптимизированными), чтобы такие методы заработали. Сейчас же выбор один — алгоритмическое кэширование.

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >