Статьи

Автор: Мациевский Николай aka sunnybear
Опубликована: 18 июня 2008

Оптимизация размера файлов: уплотняем поток

После ряда статей на тему минимизации размера файлов и распределения их по нескольким хостам у меня возник вопрос: какое оптимальное соотношение между числом (или размером) «встроенных» и внешних файлов? Какая часть страницы должна загружаться вместе с основным HTML-файлом, а какая — только с внешними файлами? Для решения этих и ряда других вопросов я собрал тестовое окружение в виде одной странице, для которой применены различные оптимизационные техники (заодно и посмотрел, как реально все эти техники влияют на скорость загрузки страницы).

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

Начал я с обычной страницы, для которой использовалось только gzip-сжатие HTML-файла. Это самое простое, что может быть сделано для оптимизации страницы (на самом деле, причиной было то, что мне не хотелось специально отключать сжатие для одного хоста, а потом его включать обратно :). Данная страница бралась за основу, с которой сравнивалось все остальное.

Для тестов я взял главную страницу конкурса WebHiTech и немного добавил туда картинок (чтобы было больше внешних объектов, и размер страницы увеличился). В итоге у меня получилось что-то следующее:

webo.in/tests/load-flow-slices/stage1/

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

Но этот пример очень простой, перейдем к следующим шагам

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

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

webo.in/tests/load-flow-slices/stage2/

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

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

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

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

webo.in/tests/load-flow-slices/stage3/

Данная страница не работает в IE, однако, это мне и не требовалось (я писал уже, что для IE можно использовать альтернативные data:URL методы). Больше всего меня смутило то, что страница стала грузиться не сильно быстрее. Объяснение этому чуть дальше.

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

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

webo.in/tests/load-flow-slices/stage4/

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

Шаг пятый: нарезаем поток фреймами

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

webo.in/tests/load-flow-slices/stage5/

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

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

webo.in/tests/load-flow-slices/stage6/

В результате, удалось уменьшить время загрузки еще на 5%, итоговое ускорение (в случае полного кеша) достигло 20%, размер страницы при этом уменьшился на 21%. Оптимальное решение выглядит примерно следующим образом:

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

Все ссылки и результаты также приведены на соответствующей странице. Для тестов использовался Firefox 3.

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

Адрес страницыОписаниеОбщий размер (кб)Время загрузки (мс)
stage1Обычная страница. Ничего не сжато (только html отдается через gzip)63117
stage2HTML/CSS файлы и картинки минимизированы58108
stage3Один-единственный файл. Картинки вставлены через data:URL49104
stage4HTML-файл параллельно загружает 6 чанков с данными и собирает на клиенте49233
stage5HTML-файл загружает 4 iframe49205
stage6Вариант #3, только JPEG-изображения (примерно одинаковые по размеру) вынесены в файлы и загружаются через (new Image()).src в head странице4998

Маленькие хитрости

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

Первое — это включение CSS-файла в HTML. Да, это увеличивает трафик пользователей и не позволяет кешировать таблицу стилей, однако, благодаря именно такому подходу, сама страница загружается в браузере поразительно быстро (Яндекс, кстати, придерживается того же мнения). Ведь отображение страницы начнется только после загрузки всех стилей, а если для этого нужен дополнительный запрос на сервер, то время ожидания может сильно затянуться.

Второе — это техника предзагрузки картинок. Я использую просто new Image().src='my.image.src' в самом начале страницы (еще до того, как браузер дошел до таблицы стилей или вызовов картинок внутри документа). Если у вас картинки, которые вы собираетесь загружать через внешние файлы, подставляются в документ динамически, то имеет смысл также динамически создавать массив для их предзагрузки в head, в противном случае у вас это просто статический список.

Заключение

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

Читать дальше

Все комментарии (habrahabr.ru)