Одним из наиболее фундаментальных правил разработки быстрых веб-сайтов является оптимизация их ресурсов. Если речь идёт о текстовых ресурсах — о коде, написанном на HTML, CSS и JavaScript, это значит, что мы говорим о сжатии данных.
![](https://habrastorage.org/webt/f1/0c/go/f10cgoennqxp98xrt8hxus_wkii.jpeg)
Стандартом де-факто в деле сжатия текстовых ресурсов в веб является метод Gzip. А именно, около 80% сжатых ресурсов, получаемых при загрузках сайтов, сжаты с использованием Gzip. Для сжатия оставшихся 20% ресурсов используется гораздо более новый алгоритм — Brotli.
Конечно, в эти 100% сжатых ресурсов, поступающих в браузеры при получении ответов на запросы к сайтам, не входят абсолютно все ресурсы. Всё ещё существует множество ресурсов, которые могли бы быть сжаты, или которые стоило бы сжать. Но эти ресурсы так и остаются несжатыми. Более детальные показатели, касающиеся сжатия, можно найти в разделе Compression ресурса Web Almanac.
Метод сжатия Gzip невероятно эффективен. Все работы Шекспира в виде обычного текста занимают 5.3 Мб. А после Gzip-сжатия (уровень сжатия 6) они занимают всего 1.9 Мб. Это значит, что размер файла, в котором хранятся эти данные, уменьшился в 2.8 раза. При этом данные при сжатии не теряются. Замечательно!
Ещё лучше то, что на степень Gzip-сжатия влияет наличие в файлах повторяющихся строк. Чем больше в тексте повторов — тем эффективнее и сжатие. Это очень хорошо для веба, так как код, написанный на HTML, CSS и JS, отличается единообразным синтаксисом и содержит много повторений.
Но, хотя Gzip — весьма эффективный метод сжатия, он ещё и довольно старый. Он появился в 1992 году (что, конечно, помогает объяснить его широчайшую распространённость). Через 21 год, в 2013 году, компания Google выпустила Brotli — новый алгоритм, который обещает даже более высокие уровни сжатия, чем те, на которые способен метод Gzip. Те же работы Шекспира размером 5.2 Мб сжимаются с помощью Brotli до размера 1.7 Мб (с уровнем сжатия 6). А это уже означает уменьшение размеров файла в 3.1 раза. Это — ещё лучше, чем при использовании Gzip.
Используя инструмент для оценки уровня сжатия данных с использованием Gzip и Brotli, вы, вполне вероятно, выясните, что при сжатии некоторых данных Brotli оказывается намного эффективнее Gzip. Например, ReactDOM-данные оказываются на 27% меньше при сжатии с использованием Brotli с максимальным уровнем сжатия (11), чем при использовании Gzip с максимальным уровнем сжатия (9).
Вот сравнение Brotli-сжатия с Gzip-сжатием при обработке ReactDOM.
Как видите, на всех уровнях сжатия Brotli обходит Gzip. На максимальном уровне сжатия, доступном при использовании Brotli, он оказывается на 27% эффективнее Gzip.
И, на основе личного наблюдения, отмечу, что переход одного моего клиента с Gzip на Brotli привёл к медианному уменьшению размеров файлов на 31%.
В результате в последние несколько лет я, вместе с другими специалистами по производительности, рекомендую клиентам переходить с Gzip на Brotli.
Скажу пару слов о браузерной поддержке Gzip и Brotli. Gzip распространён настолько широко, что на CanIUse даже не выводится таблица со сведениями о поддержке. Там сказано так: «Этот HTTP-заголовок поддерживается практически во всех браузерах (начиная с IE6+, Firefox 2+, Chrome 1+ и так далее)». А Brotli во время написания этого материала пользуется весьма приятным уровнем поддержки в 93.17%. А это очень и очень много! Таким образом, если ваш сайт имеет хоть сколько-нибудь значительный размер, вам может не особенно понравиться отдача несжатых ресурсов более чем 6% ваших пользователей. Но, используя Brotli, вы ни в чём не проиграете. Клиенты используют прогрессивную модель поддержки новых алгоритмов, поэтому пользователи, которые не могут принимать Brotli-ресурсы, просто воспользуются запасным вариантом в виде Gzip. Подробнее об этом мы поговорим ниже.
В целом, в особенности, если вы используете CDN, включение Brotli — секундное дело. По крайней мере это так в Cloudflare, в сервисе, который я использую для сайта CSS Wizardry. Однако многие мои клиенты, если говорить о последней паре лет, не так удачливы. Некоторые из них поддерживают собственную инфраструктуру, а практика показывает, что установить и развернуть Brotli не так уж и просто. Некоторые пользуются CDN-сервисами, не отличающимися легкодоступными возможностями по поддержке нового алгоритма.
В тех случаях, когда мы не могли перейти на Brotli, перед нами всегда стоял неотвеченный вопрос: «А что если…». В результате я наконец решил вооружиться цифрами и дать ответ на вопрос о том, что даёт сайту переход на Brotli.
Обычно «меньше», всё же, означает «быстрее». Как правило, если уменьшить размер файла, то он быстрее будет передаваться от сервера клиенту. Но если сделать файл, скажем, на 20% меньше, это не значит, что он прибудет на 20% быстрее. Дело тут в том, что размер файлов — это лишь один из аспектов веб-производительности. И каким бы ни был размер файла, ресурс, доставляемый в браузер, связан с множеством других факторов и с множеством ограничений систем — это сетевые задержки, потеря пакетов и прочее подобное. Иными словами, экономия на размере файла помогает передать те же данные, что и раньше, отправив меньше пакетов, но передача данных между сервером и клиентом ограничена сетевыми задержками, в результате скорость, с которой на клиент прибудут пакеты, которых будет меньше, не изменится.
Если очень упрощённо рассмотреть передачу файлов от сервера клиенту, мы должны будем взглянуть на протокол TCP. Когда мы получаем файл с сервера, он не приходит к нам за один заход. Протокол TCP, поверх которого работает HTTP, разбивает файлы на сегменты, которые называют пакетами. Эти пакеты отправляют клиенту по порядку, в составе последовательностей. Клиент подтверждает приём каждого пакета серии перед началом передачи следующей серии. Так происходит до тех пор, пока клиент не соберёт все необходимые пакеты, до тех пор, пока на сервере не останется неотправленных пакетов, и пока клиент не сможет собрать пакеты в нечто, что можно признать файлом. Для того чтобы сеанс передачи последовательности пакетов завершился бы успешно, сервер должен отправить их клиенту, а клиент должен подтвердить их получение. Время, необходимое на передачу данных и на получение подтверждения об их приёме, называется круговой задержкой (Round Trip Time, RTT).
Каждое новое TCP-соединение не может знать о том, какова доступная ему полоса пропускания, и о том, насколько надёжно соединение (то есть — каков уровень потери пакетов). Если сервер пытается отправить мегабайты данных по соединению, поддерживающему скорость передачи данных в один мегабит в секунду, такая передача переполнит соединение и приведёт к перегрузке канала связи. И наоборот — если сервер попробует передать небольшой объём данных через очень быстрое соединение, соединение будет использоваться неэффективно, оно будет простаивать.
Для того чтобы решить эту головоломку, в TCP используется механизм, называемый медленным стартом. Это — часть стратегии управления окном перегрузки. Каждое новое TCP-соединение ограничено возможностью отправки всего 10 пакетов данных в первой последовательности пакетов (10 пакетов — размер исходного окна перегрузки). Десять TCP-сегментов — это примерно 14 Кб данных. Если эти пакеты успешно получены клиентом, вторая серия будет содержать уже 20 пакетов, затем их будет 40, 80, 160 и так далее. Экспоненциальный рост пакетов в последовательностях будет продолжаться до тех пор, пока не произойдёт одно из следующих событий:
Эта простая и изящная стратегия позволяет балансировать на грани осторожности и оптимизма. Она применяется к каждому новому TCP-соединению, устанавливаемому веб-приложением.
Если говорить простыми словами, то исходный размер окна перегрузки нового TCP-соединения составляет лишь примерно 14 Кб. Или примерно 11.8% несжатых ReactDOM-данных. Или 36.94% этих данных, сжатых с использованием Gzip, или 42.38% данных, сжатых с помощью Brotli (при максимальном уровне сжатия).
А тут притормозим. Переход от 11.8% к 36.94% — это уже весьма заметное улучшение! А вот переход от 36.94% к 42.38% — это уже далеко не так впечатляюще. Что происходит?
Оказывается, что и данные, сжатые с помощью Gzip, и данные, сжатые с помощью Brotli, передаются в одной и той же серии пакетов. На передачу файла уходит две последовательности. Если RTT при передаче всех последовательностей оказывается достаточно единообразным, это означает, что на передачу данных, сжатых с использованием Gzip и Brotli, уходит одно и то же время. С другой стороны, для передачи несжатой версии данных требуется уже четыре серии пакетов, а не две. А это, особенно на соединениях с высокими сетевыми задержками, может вылиться в довольно заметное время, необходимое на передачу данных.
Я клоню тут к тому, что скорость передачи данных зависит не только от размеров файлов. На неё влияют и особенности функционирования протокола TCP. Нам не просто нужно сделать файлы меньше. Нам нужно сделать их значительно меньше, доведя до такого размера, который позволит передать их в меньшем количестве последовательностей пакетов.
Это означает, в теории, то, что для того, чтобы алгоритм Brotli был бы заметно эффективнее Gzip, он должен быть способен сжимать данные гораздо агрессивнее. Нужно это для того, чтобы данные можно было бы передавать в меньшем количестве последовательностей пакетов, чем при использовании Gzip. И я не знаю о том, как будет развиваться этот алгоритм…
Стоит отметить, что вышеприведённая модель довольно сильно упрощена. Существует огромное количество других факторов, которые можно принять во внимание. Например — речь идёт о новом, или об уже открытом TCP-соединении? Используется ли соединение для чего-то ещё? Осуществляется ли остановка и запуск передачи данных серверными механизмами приоритизации трафика? Есть ли у H/2 потоков эксклюзивный доступ к полосе пропускания? Этот раздел представляет собой более серьёзное исследование. Его стоит рассматривать как хорошую стартовую точку для собственных изысканий. Но рассмотрите возможность тщательного анализа данных с использованием чего-то вроде Wireshark, и прочитайте этот материал, в котором представлено более глубокое описание «волшебных» первых 14 Кб.
Вышеописанное применимо только к совершенно новым TCP-соединениям. Файлы, передаваемые по существующему соединению, не будут проходить через процедуру медленного старта. Это приводит нас к двум важным выводам:
В конце 10 сеанса передачи данных объём данных, передаваемых за один сеанс, составляет 7168 Кб, при этом, в общей сложности, уже передано 14322 Кб данных. Это — более чем достаточно для обычной работы в интернете (то есть — не для просмотра «Игры престолов»). На самом деле, обычно случается так, что мы загружаем всю веб-страницу и все её ресурсы, даже не достигнув лимита нашей полосы пропускания. Другими словами, использование оптоволоконного канала связи на 1 Гбит/с (то есть — 0.125 Гбайт/с) не приведёт к тому, что обычный просмотр сайтов будет казаться гораздо быстрее, чем при использовании более медленного соединения, так как большая часть такого канала даже не будет использоваться.
А к 20 сеансу передачи данных мы, теоретически, передаём в одной последовательности пакетов 7.34 Гб данных.
До сих пор мы занимались теоретическими рассуждениями. А я приступил к работе над этим материалом из-за того, что мне хотелось бы узнать о том, какое воздействие может оказать применение Brotli на реальные сайты.
До сих пор приводимые здесь числа указывали на огромную разницу между отсутствием сжатия и использованием Gzip, и на то, что выигрыш от использования Brotli, в сравнении с Gzip, оказывается довольно скромным. Это подсказывает нам то, что переход от отсутствия сжатия на использование Gzip даст заметное улучшение, а вот переход с Gzip на Brotli может, вероятно, выглядеть куда менее впечатляюще.
Я подобрал, в качестве примеров, несколько сайтов, руководствуясь следующими соображениями:
Учитывая эти требования, я отобрал сайты, список которых приведён ниже, и приступил к тестированию. Вот сайты, которые я выбрал:
Мне не хотелось усложнять тесты, поэтому я остановился на следующих показателях:
Они анализировались в следующих ситуациях:
Метрика FCP выглядит приближенной к реальному миру и достаточно универсальной для её применения к любому сайту, так как она позволяет оценить то, что нужно от веб-сайтов людям — то есть, содержимое этих сайтов. Кроме того, я выбрал эту метрику из-за того, что Пол Кальвано, умный человек, сказал так: «Опыт подсказывает мне, что использование Brotli приводит к улучшению FCP, особенно в тех случаях, когда критические CSS/JS ресурсы имеют большие размеры».
Открою один грязный секрет. Многие исследования веб-производительности (не все, но многие) основаны не на исследовании улучшений производительности, а на построении выводов из обратного — из ухудшения производительности. Например, BBC гораздо легче заявить о том, что «они теряют 10% пользователей на каждую дополнительную секунду, необходимую для загрузки их сайта», чем выяснить то, что происходит благодаря односекундному улучшению. Гораздо легче замедлить сайт, а не ускорить, и возникает такое ощущение, что именно поэтому многие так хорошо справляются с этой задачей.
Учитывая это, я не стремился сначала загрузить сайты, на которых используется Gzip, а потом, в оффлайне, как-то сжать их содержимое с помощью Brotli. Вместо этого я нашёл сайты, на которых используется Brotli, а потом отключил сжатие. Я шёл от Brotli к Gzip, а потом — от Gzip — к отсутствию сжатия, измеряя то, как это действует на сайт.
Хотя я не могу, скажем, подключиться к серверу Linkedin и отключить Brotli, я могу просто обратиться к этому сайту из браузера, который не поддерживает Brotli. И хотя я не в состоянии отключить поддержку Brotli в Chrome, я способен скрыть от сервера тот факт, что мой браузер поддерживает Brotli. Браузеры сообщают серверам о том, какие алгоритмы сжатия они поддерживают, пользуясь заголовком запроса
![](https://habrastorage.org/getpro/habr/post_images/3bb/295/390/3bb2953909f7cea0ac8970049f763638.png)
Расширенные возможности WebPageTest позволяют нам задавать собственные заголовки запросов
Вот как я настраивал поле Custom Headers:
Узнать, работает ли это так, как задумано, можно, проверив наличие (или отсутствие) заголовка
Как и ожидалось, переход от отсутствия сжатия к Gzip означал значительное улучшение, но переход от Gzip к Brotli выглядит уже не так впечатляюще. Необработанные данные моих экспериментов можно найти здесь. Ниже приведены выводы, которые интересуют меня больше всего:
Всё это — медианные значения. Говоря о «размерах материалов», я имею в виду только HTML, CSS и JavaScript.
Благодаря использованию Gzip размеры файлов удалось уменьшить на 73% в сравнении с их несжатыми версиями. А применение Brotli позволило уменьшить размеры файлов лишь на дополнительные 5.7%. Если говорить о FCP, то благодаря Gzip этот показатель улучшился на 23% в сравнении с отсутствием сжатия, а Brotli добавил к этому лишь дополнительных 3.5%.
Хотя возникает такое впечатление, что такие результаты подкрепляют теорию, есть несколько способов улучшить эти результаты. Первый заключается в тестировании гораздо большего количества сайтов, ещё два мне хотелось бы обсудить подробнее.
В моих тестах я отключил Brotli повсюду, а не только для серверов, хранящих данные сайтов. Это означает, что я измерял не только преимущества, получаемые сайтами от использования Brotli, но, в потенциале, и преимущества, получаемые от Brotli внешними источниками, которыми пользуются эти сайты. Это попадает в сферу наших интересов только в том случае, если в критически важных путях исследуемых сайтов используются сторонние ресурсы, но об этом стоит помнить.
Говоря о сжатии, мы часто обсуждаем результаты, получаемые при наилучшем сценарии применения сжатия. А именно — при использовании Gzip имеем в виду 9 уровень сжатия, а при использовании Brotli — 11 уровень. Однако едва ли исследуемый сервер будет настроен наиболее оптимальным образом. Например, в Apache используется Gzip-сжатие 6 уровня, а в NGINX — лишь первого.
Отключение Brotli означает, что мы переходим на резервный вариант, на Gzip, и учитывая то, как я тестировал сайты, я не мог изменить такую резервную конфигурацию, или как-то на неё подействовать. Я говорю об этом из-за того, что материалы двух сайтов в тесте, на самом деле, увеличивались в размерах при включении Brotli. Мне это указывает на то, что уровень Gzip-сжатия был таким, который обеспечил более сильное сжатие, чем уровень Brotli-сжатия.
Выбор уровня сжатия — это компромиссное решение. Каждому хотелось бы задать самый высокий уровень сжатия и на этом счесть вопрос решённым. Но такой подход непрактичен. Дело в том, что дополнительное время, которое потребуется серверу для динамического выполнения такого сжатия, весьма вероятно, сведёт на нет выгоду от более высокого уровня сжатия. Для того чтобы справиться с этой проблемой, можно прибегнуть к следующему:
Создаётся такое впечатление, что, рассуждая здраво, можно признать незначительность преимуществ Brotli перед Gzip.
Если включение поддержки Brotli — это вопрос пары движений мыши в панели управления вашего CDN — то вам стоит прямо сейчас взять и включить Brotli. Поддержка этого алгоритма сжатия достаточно широка, браузеры, которые не поддерживают Brotli, легко переходят на запасные механизмы, а даже небольшое улучшение — это лучше, чем ничего.
Если это возможно — загружайте на серверы заранее сжатые на максимальном уровне сжатия статические ресурсы. А для динамического сжатия используйте не самые высокие, но и не самые низкие уровни сжатия. Если вы пользуетесь NGINX — проверьте, что у вас не используется стандартный для NGINX первый уровень сжатия.
Однако если для того, чтобы воспользоваться Brotli, вам могут понадобиться недели разработки, тестирования и развёртывания, не стоит впадать в панику — просто убедитесь в том, что применяете Gzip-сжатие для всего, что можно сжать (сюда входят, кроме текстовых ресурсов, файлы .ico и .ttf — если они, конечно, используются в вашем проекте).
Полагаю, краткая версия этой статьи может выглядеть так: если вы не должны или не можете включить Brotli — вы теряете не так уж и много.
Уважаемые читатели! Планируете ли вы пользоваться Brotli?
![](https://habrastorage.org/webt/iq/fi/b4/iqfib45pgphfrxv--zfemt0qnmw.jpeg)
![](https://habrastorage.org/webt/f1/0c/go/f10cgoennqxp98xrt8hxus_wkii.jpeg)
Стандартом де-факто в деле сжатия текстовых ресурсов в веб является метод Gzip. А именно, около 80% сжатых ресурсов, получаемых при загрузках сайтов, сжаты с использованием Gzip. Для сжатия оставшихся 20% ресурсов используется гораздо более новый алгоритм — Brotli.
Конечно, в эти 100% сжатых ресурсов, поступающих в браузеры при получении ответов на запросы к сайтам, не входят абсолютно все ресурсы. Всё ещё существует множество ресурсов, которые могли бы быть сжаты, или которые стоило бы сжать. Но эти ресурсы так и остаются несжатыми. Более детальные показатели, касающиеся сжатия, можно найти в разделе Compression ресурса Web Almanac.
Метод сжатия Gzip невероятно эффективен. Все работы Шекспира в виде обычного текста занимают 5.3 Мб. А после Gzip-сжатия (уровень сжатия 6) они занимают всего 1.9 Мб. Это значит, что размер файла, в котором хранятся эти данные, уменьшился в 2.8 раза. При этом данные при сжатии не теряются. Замечательно!
Ещё лучше то, что на степень Gzip-сжатия влияет наличие в файлах повторяющихся строк. Чем больше в тексте повторов — тем эффективнее и сжатие. Это очень хорошо для веба, так как код, написанный на HTML, CSS и JS, отличается единообразным синтаксисом и содержит много повторений.
Но, хотя Gzip — весьма эффективный метод сжатия, он ещё и довольно старый. Он появился в 1992 году (что, конечно, помогает объяснить его широчайшую распространённость). Через 21 год, в 2013 году, компания Google выпустила Brotli — новый алгоритм, который обещает даже более высокие уровни сжатия, чем те, на которые способен метод Gzip. Те же работы Шекспира размером 5.2 Мб сжимаются с помощью Brotli до размера 1.7 Мб (с уровнем сжатия 6). А это уже означает уменьшение размеров файла в 3.1 раза. Это — ещё лучше, чем при использовании Gzip.
Используя инструмент для оценки уровня сжатия данных с использованием Gzip и Brotli, вы, вполне вероятно, выясните, что при сжатии некоторых данных Brotli оказывается намного эффективнее Gzip. Например, ReactDOM-данные оказываются на 27% меньше при сжатии с использованием Brotli с максимальным уровнем сжатия (11), чем при использовании Gzip с максимальным уровнем сжатия (9).
Вот сравнение Brotli-сжатия с Gzip-сжатием при обработке ReactDOM.
Уровень сжатия | Размер (в бвайтах) | Эффективность сжатия (в сравнении с несжатыми данными) | Улучшение в % по сравнению с Gzip |
1 | 43456 | 2.73 | 3% |
2 | 39898 | 2.97 | 11% |
3 | 39416 | 3.08 | 15% |
4 | 38488 | 3.08 | 15% |
5 | 36323 | 3.27 | 19% |
6 | 36048 | 3.29 | 20% |
7 | 35804 | 3.31 | 20% |
8 | 35709 | 3.32 | 21% |
9 | 35659 | 3.33 | 21% |
10 | 33590 | 3.53 | 25% |
11 | 33036 | 3.59 | 27% |
Как видите, на всех уровнях сжатия Brotli обходит Gzip. На максимальном уровне сжатия, доступном при использовании Brotli, он оказывается на 27% эффективнее Gzip.
И, на основе личного наблюдения, отмечу, что переход одного моего клиента с Gzip на Brotli привёл к медианному уменьшению размеров файлов на 31%.
В результате в последние несколько лет я, вместе с другими специалистами по производительности, рекомендую клиентам переходить с Gzip на Brotli.
Скажу пару слов о браузерной поддержке Gzip и Brotli. Gzip распространён настолько широко, что на CanIUse даже не выводится таблица со сведениями о поддержке. Там сказано так: «Этот HTTP-заголовок поддерживается практически во всех браузерах (начиная с IE6+, Firefox 2+, Chrome 1+ и так далее)». А Brotli во время написания этого материала пользуется весьма приятным уровнем поддержки в 93.17%. А это очень и очень много! Таким образом, если ваш сайт имеет хоть сколько-нибудь значительный размер, вам может не особенно понравиться отдача несжатых ресурсов более чем 6% ваших пользователей. Но, используя Brotli, вы ни в чём не проиграете. Клиенты используют прогрессивную модель поддержки новых алгоритмов, поэтому пользователи, которые не могут принимать Brotli-ресурсы, просто воспользуются запасным вариантом в виде Gzip. Подробнее об этом мы поговорим ниже.
В целом, в особенности, если вы используете CDN, включение Brotli — секундное дело. По крайней мере это так в Cloudflare, в сервисе, который я использую для сайта CSS Wizardry. Однако многие мои клиенты, если говорить о последней паре лет, не так удачливы. Некоторые из них поддерживают собственную инфраструктуру, а практика показывает, что установить и развернуть Brotli не так уж и просто. Некоторые пользуются CDN-сервисами, не отличающимися легкодоступными возможностями по поддержке нового алгоритма.
В тех случаях, когда мы не могли перейти на Brotli, перед нами всегда стоял неотвеченный вопрос: «А что если…». В результате я наконец решил вооружиться цифрами и дать ответ на вопрос о том, что даёт сайту переход на Brotli.
«Меньше» — необязательно значит «быстрее»
Обычно «меньше», всё же, означает «быстрее». Как правило, если уменьшить размер файла, то он быстрее будет передаваться от сервера клиенту. Но если сделать файл, скажем, на 20% меньше, это не значит, что он прибудет на 20% быстрее. Дело тут в том, что размер файлов — это лишь один из аспектов веб-производительности. И каким бы ни был размер файла, ресурс, доставляемый в браузер, связан с множеством других факторов и с множеством ограничений систем — это сетевые задержки, потеря пакетов и прочее подобное. Иными словами, экономия на размере файла помогает передать те же данные, что и раньше, отправив меньше пакетов, но передача данных между сервером и клиентом ограничена сетевыми задержками, в результате скорость, с которой на клиент прибудут пакеты, которых будет меньше, не изменится.
TCP, пакеты, круговая задержка
Если очень упрощённо рассмотреть передачу файлов от сервера клиенту, мы должны будем взглянуть на протокол TCP. Когда мы получаем файл с сервера, он не приходит к нам за один заход. Протокол TCP, поверх которого работает HTTP, разбивает файлы на сегменты, которые называют пакетами. Эти пакеты отправляют клиенту по порядку, в составе последовательностей. Клиент подтверждает приём каждого пакета серии перед началом передачи следующей серии. Так происходит до тех пор, пока клиент не соберёт все необходимые пакеты, до тех пор, пока на сервере не останется неотправленных пакетов, и пока клиент не сможет собрать пакеты в нечто, что можно признать файлом. Для того чтобы сеанс передачи последовательности пакетов завершился бы успешно, сервер должен отправить их клиенту, а клиент должен подтвердить их получение. Время, необходимое на передачу данных и на получение подтверждения об их приёме, называется круговой задержкой (Round Trip Time, RTT).
Каждое новое TCP-соединение не может знать о том, какова доступная ему полоса пропускания, и о том, насколько надёжно соединение (то есть — каков уровень потери пакетов). Если сервер пытается отправить мегабайты данных по соединению, поддерживающему скорость передачи данных в один мегабит в секунду, такая передача переполнит соединение и приведёт к перегрузке канала связи. И наоборот — если сервер попробует передать небольшой объём данных через очень быстрое соединение, соединение будет использоваться неэффективно, оно будет простаивать.
Для того чтобы решить эту головоломку, в TCP используется механизм, называемый медленным стартом. Это — часть стратегии управления окном перегрузки. Каждое новое TCP-соединение ограничено возможностью отправки всего 10 пакетов данных в первой последовательности пакетов (10 пакетов — размер исходного окна перегрузки). Десять TCP-сегментов — это примерно 14 Кб данных. Если эти пакеты успешно получены клиентом, вторая серия будет содержать уже 20 пакетов, затем их будет 40, 80, 160 и так далее. Экспоненциальный рост пакетов в последовательностях будет продолжаться до тех пор, пока не произойдёт одно из следующих событий:
- Система столкнётся с потерей пакетов. В этот момент сервер уменьшит количество пакетов в следующей последовательности, разделив предыдущее количество пакетов на 2, и попробует снова передать данные.
- Мы достигли предела доступной полосы пропускания и можем пользоваться ей на полную мощность.
Эта простая и изящная стратегия позволяет балансировать на грани осторожности и оптимизма. Она применяется к каждому новому TCP-соединению, устанавливаемому веб-приложением.
Если говорить простыми словами, то исходный размер окна перегрузки нового TCP-соединения составляет лишь примерно 14 Кб. Или примерно 11.8% несжатых ReactDOM-данных. Или 36.94% этих данных, сжатых с использованием Gzip, или 42.38% данных, сжатых с помощью Brotli (при максимальном уровне сжатия).
А тут притормозим. Переход от 11.8% к 36.94% — это уже весьма заметное улучшение! А вот переход от 36.94% к 42.38% — это уже далеко не так впечатляюще. Что происходит?
Номер сеанса передачи данных | Объём данных, передаваемых за один сеанс, Кб | Кумулятивный объём переданных данных, Кб | Последовательность, в которой переданы ReactDOM-данные |
1 | 14 | 14 | |
2 | 28 | 42 | Gzip (37.904 Кб), Brotli (33.036 Кб) |
3 | 56 | 98 | |
4 | 112 | 210 | Несжатый вариант (118.656 Кб) |
5 | 224 | 434 |
Оказывается, что и данные, сжатые с помощью Gzip, и данные, сжатые с помощью Brotli, передаются в одной и той же серии пакетов. На передачу файла уходит две последовательности. Если RTT при передаче всех последовательностей оказывается достаточно единообразным, это означает, что на передачу данных, сжатых с использованием Gzip и Brotli, уходит одно и то же время. С другой стороны, для передачи несжатой версии данных требуется уже четыре серии пакетов, а не две. А это, особенно на соединениях с высокими сетевыми задержками, может вылиться в довольно заметное время, необходимое на передачу данных.
Я клоню тут к тому, что скорость передачи данных зависит не только от размеров файлов. На неё влияют и особенности функционирования протокола TCP. Нам не просто нужно сделать файлы меньше. Нам нужно сделать их значительно меньше, доведя до такого размера, который позволит передать их в меньшем количестве последовательностей пакетов.
Это означает, в теории, то, что для того, чтобы алгоритм Brotli был бы заметно эффективнее Gzip, он должен быть способен сжимать данные гораздо агрессивнее. Нужно это для того, чтобы данные можно было бы передавать в меньшем количестве последовательностей пакетов, чем при использовании Gzip. И я не знаю о том, как будет развиваться этот алгоритм…
Стоит отметить, что вышеприведённая модель довольно сильно упрощена. Существует огромное количество других факторов, которые можно принять во внимание. Например — речь идёт о новом, или об уже открытом TCP-соединении? Используется ли соединение для чего-то ещё? Осуществляется ли остановка и запуск передачи данных серверными механизмами приоритизации трафика? Есть ли у H/2 потоков эксклюзивный доступ к полосе пропускания? Этот раздел представляет собой более серьёзное исследование. Его стоит рассматривать как хорошую стартовую точку для собственных изысканий. Но рассмотрите возможность тщательного анализа данных с использованием чего-то вроде Wireshark, и прочитайте этот материал, в котором представлено более глубокое описание «волшебных» первых 14 Кб.
Вышеописанное применимо только к совершенно новым TCP-соединениям. Файлы, передаваемые по существующему соединению, не будут проходить через процедуру медленного старта. Это приводит нас к двум важным выводам:
- Не думаю, что это стоит повторять, но я повторюсь: статические ресурсы нужно хостить у себя. Это — отличный способ избежать задержек медленного старта, так как использование собственного, уже «прогретого» сервера означает, что у пакетов, уходящих с этого сервера, есть доступ к более широкой полосе пропускания. Этот вывод ведёт меня ко второму выводу.
- Легко заметить, что экспоненциальный рост окна перегрузки ведёт к очень быстрому достижению сравнительно серьёзных объёмов данных, передаваемых за один сеанс. Чем больше мы используем или переиспользуем соединение, тем дальше мы можем наращивать пропускную способность до достижения ей пиковых значений. Взгляните на следующую таблицу.
Номер сеанса передачи данных | Объём данных, передаваемых за один сеанс, Кб | Кумулятивный объём переданных данных, Кб |
1 | 14 | 14 |
2 | 28 | 42 |
3 | 56 | 98 |
4 | 112 | 210 |
5 | 224 | 434 |
6 | 448 | 882 |
7 | 896 | 1778 |
8 | 1792 | 3570 |
9 | 3584 | 7154 |
10 | 7168 | 14322 |
… | … | … |
20 | 7340032 | 14680050 |
В конце 10 сеанса передачи данных объём данных, передаваемых за один сеанс, составляет 7168 Кб, при этом, в общей сложности, уже передано 14322 Кб данных. Это — более чем достаточно для обычной работы в интернете (то есть — не для просмотра «Игры престолов»). На самом деле, обычно случается так, что мы загружаем всю веб-страницу и все её ресурсы, даже не достигнув лимита нашей полосы пропускания. Другими словами, использование оптоволоконного канала связи на 1 Гбит/с (то есть — 0.125 Гбайт/с) не приведёт к тому, что обычный просмотр сайтов будет казаться гораздо быстрее, чем при использовании более медленного соединения, так как большая часть такого канала даже не будет использоваться.
А к 20 сеансу передачи данных мы, теоретически, передаём в одной последовательности пакетов 7.34 Гб данных.
Как насчёт реального мира?
До сих пор мы занимались теоретическими рассуждениями. А я приступил к работе над этим материалом из-за того, что мне хотелось бы узнать о том, какое воздействие может оказать применение Brotli на реальные сайты.
До сих пор приводимые здесь числа указывали на огромную разницу между отсутствием сжатия и использованием Gzip, и на то, что выигрыш от использования Brotli, в сравнении с Gzip, оказывается довольно скромным. Это подсказывает нам то, что переход от отсутствия сжатия на использование Gzip даст заметное улучшение, а вот переход с Gzip на Brotli может, вероятно, выглядеть куда менее впечатляюще.
Я подобрал, в качестве примеров, несколько сайтов, руководствуясь следующими соображениями:
- Сайт должен быть сравнительно известным (лучше использовать сайты, которые можно с чем-то сопоставить).
- Сайт должен подходить для теста. То есть — он должен быть подходящего размера (так его материалы будет интереснее анализировать на предмет сжатия), и при этом не должен содержать, в основном, материалы, которые не сжимают с использованием Gzip/Brotli — вроде, например, YouTube.
- Не все сайты из подборки должны принадлежать крупным корпорациям (стоит проанализировать и некоторые, скажем так, «обычные» сайты).
Учитывая эти требования, я отобрал сайты, список которых приведён ниже, и приступил к тестированию. Вот сайты, которые я выбрал:
- m.facebook.com
- www.linkedin.com
- www.insider.com
- yandex.com
- www.esedirect.co.uk
- www.story.one
- www.cdm-bedrijfskleding.nl
- www.everlane.com
Мне не хотелось усложнять тесты, поэтому я остановился на следующих показателях:
- Объём переданных данных.
- Время первой отрисовки контента (First contentful paint, FCP).
Они анализировались в следующих ситуациях:
- Отсутствие сжатия.
- Использование Gzip.
- Использование Brotli.
Метрика FCP выглядит приближенной к реальному миру и достаточно универсальной для её применения к любому сайту, так как она позволяет оценить то, что нужно от веб-сайтов людям — то есть, содержимое этих сайтов. Кроме того, я выбрал эту метрику из-за того, что Пол Кальвано, умный человек, сказал так: «Опыт подсказывает мне, что использование Brotli приводит к улучшению FCP, особенно в тех случаях, когда критические CSS/JS ресурсы имеют большие размеры».
Тестирование
Открою один грязный секрет. Многие исследования веб-производительности (не все, но многие) основаны не на исследовании улучшений производительности, а на построении выводов из обратного — из ухудшения производительности. Например, BBC гораздо легче заявить о том, что «они теряют 10% пользователей на каждую дополнительную секунду, необходимую для загрузки их сайта», чем выяснить то, что происходит благодаря односекундному улучшению. Гораздо легче замедлить сайт, а не ускорить, и возникает такое ощущение, что именно поэтому многие так хорошо справляются с этой задачей.
Учитывая это, я не стремился сначала загрузить сайты, на которых используется Gzip, а потом, в оффлайне, как-то сжать их содержимое с помощью Brotli. Вместо этого я нашёл сайты, на которых используется Brotli, а потом отключил сжатие. Я шёл от Brotli к Gzip, а потом — от Gzip — к отсутствию сжатия, измеряя то, как это действует на сайт.
Хотя я не могу, скажем, подключиться к серверу Linkedin и отключить Brotli, я могу просто обратиться к этому сайту из браузера, который не поддерживает Brotli. И хотя я не в состоянии отключить поддержку Brotli в Chrome, я способен скрыть от сервера тот факт, что мой браузер поддерживает Brotli. Браузеры сообщают серверам о том, какие алгоритмы сжатия они поддерживают, пользуясь заголовком запроса
content-encoding
. Я, пользуясь Webpagetest, могу сам настраивать заголовки. Так что, всё очень просто!![](https://habrastorage.org/getpro/habr/post_images/3bb/295/390/3bb2953909f7cea0ac8970049f763638.png)
Расширенные возможности WebPageTest позволяют нам задавать собственные заголовки запросов
Вот как я настраивал поле Custom Headers:
- Полное отключение сжатия:
accept-encoding: randomstring
. - Отключение Brotli, но поддержка Gzip:
accept-encoding: gzip
. - Для использования Brotli в том случае, если этот метод сжатия поддерживается сайтом (и при условии его поддержки браузером): поле остаётся пустым.
Узнать, работает ли это так, как задумано, можно, проверив наличие (или отсутствие) заголовка
content-encoding
в ответе сервера.Результаты
Как и ожидалось, переход от отсутствия сжатия к Gzip означал значительное улучшение, но переход от Gzip к Brotli выглядит уже не так впечатляюще. Необработанные данные моих экспериментов можно найти здесь. Ниже приведены выводы, которые интересуют меня больше всего:
- Уменьшение размеров материалов при использовании Gzip в сравнении с отсутствием сжатия: 73%.
- Улучшение показателя FCP при использовании Gzip в сравнении с отсутствием сжатия: 23.305%.
- Уменьшение размеров материалов при использовании Brotli в сравнении с Gzip: 5.767%.
- Улучшение показателя FCP при использовании Brotli в сравнении с Gzip: 3.462%.
Всё это — медианные значения. Говоря о «размерах материалов», я имею в виду только HTML, CSS и JavaScript.
Благодаря использованию Gzip размеры файлов удалось уменьшить на 73% в сравнении с их несжатыми версиями. А применение Brotli позволило уменьшить размеры файлов лишь на дополнительные 5.7%. Если говорить о FCP, то благодаря Gzip этот показатель улучшился на 23% в сравнении с отсутствием сжатия, а Brotli добавил к этому лишь дополнительных 3.5%.
Хотя возникает такое впечатление, что такие результаты подкрепляют теорию, есть несколько способов улучшить эти результаты. Первый заключается в тестировании гораздо большего количества сайтов, ещё два мне хотелось бы обсудить подробнее.
Собственные данные ресурсов и данные из внешних источников
В моих тестах я отключил Brotli повсюду, а не только для серверов, хранящих данные сайтов. Это означает, что я измерял не только преимущества, получаемые сайтами от использования Brotli, но, в потенциале, и преимущества, получаемые от Brotli внешними источниками, которыми пользуются эти сайты. Это попадает в сферу наших интересов только в том случае, если в критически важных путях исследуемых сайтов используются сторонние ресурсы, но об этом стоит помнить.
Уровни сжатия
Говоря о сжатии, мы часто обсуждаем результаты, получаемые при наилучшем сценарии применения сжатия. А именно — при использовании Gzip имеем в виду 9 уровень сжатия, а при использовании Brotli — 11 уровень. Однако едва ли исследуемый сервер будет настроен наиболее оптимальным образом. Например, в Apache используется Gzip-сжатие 6 уровня, а в NGINX — лишь первого.
Отключение Brotli означает, что мы переходим на резервный вариант, на Gzip, и учитывая то, как я тестировал сайты, я не мог изменить такую резервную конфигурацию, или как-то на неё подействовать. Я говорю об этом из-за того, что материалы двух сайтов в тесте, на самом деле, увеличивались в размерах при включении Brotli. Мне это указывает на то, что уровень Gzip-сжатия был таким, который обеспечил более сильное сжатие, чем уровень Brotli-сжатия.
Выбор уровня сжатия — это компромиссное решение. Каждому хотелось бы задать самый высокий уровень сжатия и на этом счесть вопрос решённым. Но такой подход непрактичен. Дело в том, что дополнительное время, которое потребуется серверу для динамического выполнения такого сжатия, весьма вероятно, сведёт на нет выгоду от более высокого уровня сжатия. Для того чтобы справиться с этой проблемой, можно прибегнуть к следующему:
- Можно использовать прагматичный уровень сжатия, который обеспечивает правильный баланс скорости и эффективности в ходе динамического сжатия данных.
- Можно загрузить на сервер заранее сжатые статические ресурсы, уровень сжатия которых выше, чем тот, который используется для динамического сжатия. При этом для подбора уровня динамического сжатия можно воспользоваться идеей, изложенной в первом пункте.
Итоги
Создаётся такое впечатление, что, рассуждая здраво, можно признать незначительность преимуществ Brotli перед Gzip.
Если включение поддержки Brotli — это вопрос пары движений мыши в панели управления вашего CDN — то вам стоит прямо сейчас взять и включить Brotli. Поддержка этого алгоритма сжатия достаточно широка, браузеры, которые не поддерживают Brotli, легко переходят на запасные механизмы, а даже небольшое улучшение — это лучше, чем ничего.
Если это возможно — загружайте на серверы заранее сжатые на максимальном уровне сжатия статические ресурсы. А для динамического сжатия используйте не самые высокие, но и не самые низкие уровни сжатия. Если вы пользуетесь NGINX — проверьте, что у вас не используется стандартный для NGINX первый уровень сжатия.
Однако если для того, чтобы воспользоваться Brotli, вам могут понадобиться недели разработки, тестирования и развёртывания, не стоит впадать в панику — просто убедитесь в том, что применяете Gzip-сжатие для всего, что можно сжать (сюда входят, кроме текстовых ресурсов, файлы .ico и .ttf — если они, конечно, используются в вашем проекте).
Полагаю, краткая версия этой статьи может выглядеть так: если вы не должны или не можете включить Brotli — вы теряете не так уж и много.
Уважаемые читатели! Планируете ли вы пользоваться Brotli?
![](https://habrastorage.org/webt/iq/fi/b4/iqfib45pgphfrxv--zfemt0qnmw.jpeg)
chemtech
Тестировал Brotli при сжатии бекапов PostgreSQL при использовании WAL-G:
![image](https://habrastorage.org/webt/ry/zd/p6/ryzdp6nq8jdqbextit_kzisxvd4.png)
Brotli сопоставим по размеру с LZMA, но бекап выполняется за время LZ4.
ornic
а фейсбучий Zstandard сравнивали?
chemtech
В WAL-G нет поддержки Zstandard
domix32
В браузерах нет zstd
ornic
LZMA и LZ4 тоже нет :) хотя понятно что они тут в качестве референсов приводились
Но вообще говоря "в обратную сторону" тоже важно бывает пожать данные. И сейчас, похоже, gzip без альтернатив тут, потому что brotli и Zstandard никто не хочет реализовывать на js, потому что если "очень надо", то есть порт (кажется, через WebAssembly).
domix32
упустил как-то из внимание, что wal-g не про браузеры.
в смысле разжать? Не понял куда эта обратная сторона.
Реализовывать zstd на js дело довольно неблагодарное, и нетривиальное — весь профит от распараллеливания и оптимизаций скомпилированной версии нивелируются скоростью работы самого JS и его сборщика мусора. C wasm оно конечно побыстрее, тут недавно еще и simd подвезли (правда пока под флагом), но стоит ли овчинка выделки — вопрос довольно большой. Скомпилировать исходники в js/wasm без оптимизаций вроде можно без больших проблем.
ornic
В обратную сторону — это не передавать с фронта на бэк мегабайты несжатых данных.
Ну brotli тоже не хотят на js реализовывать. :) Но да, я думаю там примерно те же проблемы, только с другого боку.