Высокая производительность Google Chrome

История и краеугольные принципы Google Chrome.

xcomic-chrome.png.pagespeed.ic_.viIocNBp3uGoogle Chrome был представлен во второй половине 2008 года, как бета версия для Windows платформы. Код Chrome, авторство которого принадлежит Google, был сделан доступным под либеральной BSD лицензией — как и Chromium проект. Для большинства заинтересованных, такой поворот событий стал сюрпризом — война браузеров возвращается? Сможет ли Google сделать свой продукт действительно лучше других?

«Это было столь хорошо, что заставило меня изменить свое мнение..» — Эрих Шмидт, первоначально не желающий принимать идею Google Chrome.


Да, он смог! Сегодня Google Chrome является одним из наиболее популярных браузеров (35% доля рынка, показатели StatCounter) и доступен на Windows, Linux, OS X, Chrome OS, Android и iOS платформах. Несомненно, его достоинства и широкий функционал нашли отклик в сердцах пользователей, подарив много замечательных идей другим браузерам.

Оригинал 38 страничной книги комиксов, с разъяснениями идей и принципов Google Chrome предлагает нам замечательный пример мышления и процесса проектирования, результатом которых стал этот браузер. Однако, это только начало пути. Главные принципы, которые стали мотиваторами первых этапов разработки Chrome, нашли свое продолжение и в правилах непрерывного усовершенствования браузера:

  • Скорость: сделать самый быстрый браузер
  • Безопасность: предоставить пользователю наиболее защищенную среду для работы
  • Стабильность: предоставить гибкую и стабильную платформу для веб-приложений
  • Простота: сложные технологии за простым интерфейсом.

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

Многогранность производительности.

Современные браузеры представляют собой платформу, которая во многом напоминает операционную систему, и Google Chrome не исключение. Браузеры, которые предшествовали Chrome, были спроектированы как монолитные программы, с одним рабочим процессом. Все открытые страницы делили между собой одно адресное пространство и работали с одними и теми же ресурсами, поэтому ошибка в обработке любой страницы или в механизме браузера, грозила сбоями и крахом всего приложения.
xcomic-multi-process.png.pagespeed.ic_.WG8U1j8zqv
В отличие от этого подхода, в основе Chrome лежит мульти-процессная архитектура, которая предоставляет каждой странице свой отдельный процесс и память, создавая что-то вроде жестко изолированной песочницыдля каждой вкладки. В мире все возрастающей мульти-ядерности процессоров, способность изолировать процессы одновременно с защитой каждой страницы от других, работающих из ошибками, страниц, дала Chrome значительное преимущество в производительности по сравнению с конкурентами. Стоит заметить, что большинство других браузеров последовало их примеру, внедрив или начав внедрение упомянутой архитектуры.

С разделением процессов, исполнение веб-приложения главным образом включает три задачи: получить все нужные ресурсы, построить структуру страницы и отобразить ее, выполнить JS. Процессы построения страницы и выполнения JS следуют однопоточной, чередующей схеме, поскольку невозможно выполнить одновременное построение и модификацию одного и того же дерева страницы (DOM). Эта особенность объясняется тем фактом что JS сам по себе — однопоточный язык. Следовательно, оптимизация совместного построения страницы и исполнения скриптов в реальном времени является очень важной задачей, как для разработчиков веб-приложений, так и для разработчиков самого браузера.

Для отображения страниц Chrome использует WebKit, быстрый, открытый (Open Source) и совместимый со стандартами движок. Для выполнения JS Chrome использует собственный, очень хорошо оптимизированныйV8 Javascript движок, который, кстати, является Open Source проектом, и нашел свое применение в множестве других популярных проектов — к примеру, в node.js. Однако, оптимизация выполнения скриптов V8, или обработки и отображения страниц вебкитом не столь важны, когда браузер находится в ожидании ресурсов, необходимых для построения страницы.

Способность браузера оптимизировать порядок, приоритет, и управлять возможными задержками каждого необходимого ресурса является одним из наиболее важных факторов его работы. Вы можете даже не подозревать об этом, но сетевой интерфейс Chrome, выражаясь образно, умнеет с каждым днем, пытаясь скрыть или максимально уменьшить стоимость ожидания загрузки каждого ресурса: он учится подобно DNS поиску, запоминая топологию сети, выполняя предварительные запросы до наиболее вероятных к посещению страниц, и прочая. Внешне, он являет собой простой механизм для запроса и получения ресурсов, но его внутреннее устройство дает нам увлекательную возможность изучить, как нужно оптимизировать веб производительность чтобы оставить пользователю только лучшие впечатления.

Что такое современное веб-приложение?

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

Проект HTTP Archive сохраняет историю эволюции веб, и он поможет нам ответить на этот вопрос. Вместо того, чтобы собирать и анализировать контент со всей сети, он периодически навещает популярные сайты, чтобы собрать и записать данные о использованных ресурсах, типах контента, заголовках, и других мета-данных для каждого отдельного сайта. Статистика, доступная на январь 2013, может удивить вас. Средняя страница, с выборки среди первых 300 000 сайтов интернета, имеет такие характеристики:

xhttparchive-jan2013.png.pagespeed.ic_.dBi1KPmYR5

  • «Вес» — 1280 КB
  • Состоит из 88 ресурсов (картинки, css, js)
  • Использует данные более чем из 30 сторонних сайтов.

Давайте посмотрим на это более детально. Свыше 1MB веса в среднем, состоит из 88 ресурсов, и собирается из 30 различных собственных и сторонних серверов. Заметим, что каждый из этих показателейнеуклонно растет в течении нескольких последних лет, и нет повода прогнозировать, что этот рост остановится. Мы в все большем количестве строим все более громоздкие и требовательные веб-приложения, и этому не видно конца.

Сделав несложные математические расчеты на основе показателей HTTP Archive, можно увидеть, что средний ресурс страницы имеет вес 12KB (1045KB / 84), а это значит, что большинство интернет-соединений в браузере кратковременные и импульсивные . Это еще больше усложняет нам жизнь, поскольку лежащий в основе протокол (TCP) оптимизирован под большие, потоковые загрузки. Поэтому стоит докопаться до сути вещей, и рассмотреть один из типичных запросов на типичный ресурс.

Жизнь типичного запроса

Спецификация W3C Navigation Timing обеспечивает API браузера и возможность отследить часовые рамки и производительность каждого запроса. Давайте подробней рассмотрим его компоненты, так каждый из них представляет очень важную часть в общем впечатлении пользователя от производительности браузера.

xnavtiming.png.pagespeed.ic_.BcXT6w65LY

Получив URL ресурса в интернете, браузер начинает проверку, не локальный ли он, и имеются ли сохраненные данные в кэше. Если вы перед этим уже получали данные с этого ресурса, исоответствующие заголовки браузера были установлены (Expires, Cache-Controle, …) то есть возможность получить все данные из кэша — быстрейший запрос — это тот запрос, который не был сделан. В другом случае, если мы проверили ресурс и кэш «протух» или мы еще не посещали сайт, приходит очередь сделать дорогостоящий сетевой запрос.

Имея адресу сайта и путь к запрашиваемому ресурсу, Chrome сначала проверяет, имеются ли открытые соединения на этот сайт, которые можно использовать снова — сокеты, объединены в группы по {scheme, host, port}. В случае, если вы выходите в интернет через прокси, или установили у себя proxy auto-config (PAC) скрипт, Chrome проверяет наличие нужного соединение через соответствующий прокси. PAC скрипт позволяет задать несколько прокси, базируясь на URL или других правилах настройки конфигурации, и каждый из них может иметь свой собственный набор соединений. И наконец, если ничто из вышеуказанных условий не подошло, пришел черед получения IP адреса для нужного нам адреса — DNS Lookup.

Если нам повезло, и адрес находится в кэше, ответ наверняка обойдется нам в один быстрый системный запрос. Если нет, то первым делом нужно выполнить запрос к DNS серверу. Время, которое потребуется для его выполнения, зависит от вашего интернет провайдера, популярности запрашиваемого сайта и вероятности, что имя сайта находится в промежуточном кэше, плюс время ответа сервера DNS на данный запрос. Другими словами, здесь много неопределенности, но время в несколько сот миллисекунд, которое понадобится на запрос до DNS, не будет чем-то из ряда вон выходящим.
xtcp-handshake.png.pagespeed.ic_.41IsyjCjFX
Получив IP, Chrome может устанавливать новое TCP соединение к удаленному серверу, а это значит, что мы должны выполнить так называемое three-way handshake (трехкратное приветствие): SYN > SYN-ACK > ACK. Этот обмен приветствиями добавляет задержку на запрос-ответ для каждого нового TCP соединения — без исключений. В зависимости от расстояния между клиентом и сервером, учитывая выбор пути маршрутизации, это может отнять в нас несколько сот и даже тысяч миллисекунд. Заметим, что вся эта робота выполняется перед тем, как даже один байт данных веб-приложения будет передан!

Если TCP соединение установлено, и мы используем защищенный протокол передачи данных (HTTPS), дополнительно потребуется установить соединение по SSL. Это может отнять до двух дополнительных полных циклов вопрос-ответ между клиентом и сервером. Если SSL сессия сохранилась в кэше, мы можем обойтись только одним дополнительным циклом.

Наконец, после всех процедур, Chrome имеет возможность наконец отправить HTTP запрос (requestStart в диаграмме выше). Получив запрос, сервер начинает его обработку, и отправляет ответ назад клиенту. Это потребует минимум один цикл, плюс время на обработку запроса на сервере. И вот, мы наконец получили ответ. Да, если этот ответ не есть HTTP редирект! В таком случае нам придется повторить еще раз всю вышеописанную процедуру целиком. На ваших страницах есть парочка не совсем нужных редиректов? Наверное, стоит вернутся к ним, и поменять ваше решение.

Вы считали все эти задержки? Чтобы проиллюстрировать проблему, допустим худший сценарий типичного широкополосного соединения: локальный кэш утерян, за ним следует относительно быстрый DNS поиск (50мс), TCP приветствие, SSL переговоры, и относительно быстрый (100мс) ответ сервера, с 80мс на доставку запроса и ответа (среднее время цикла на континентальной Америке):

  • 50 мс для DNS
  • 80 мс для DNS приветствия (один цикл)
  • 160 мс для SSL приветствия (два цикла)
  • 40мс на запрос до сервера
  • 100 мс на обработку запроса на сервере
  • 40 мс на ответ из сервера

В суме это 470 мс для одиночного запроса, что приводит к затратам более чем 80% времени на установление соединения с сервером в сравнении с временем, которое нужно серверу для обработки запроса. На самом деле, даже 470 миллисекунд могут быть оптимистичной оценкой:

  • если ответ сервера не вмещается в начальное congestion окно (4-15KB), то потребуются еще несколько циклов запрос-ответ.
  • SSL задержка может быть даже хуже, если нам надо получить утерянный сертификат или выполнитьонлайн проверку статуса сертификата, в каждом случае потребуется установить новое, самостоятельное, TCP соединение, которое может добавить сотни миллисекунд или даже секунды к задержке.

Что значит «Достаточно быстро»?

Сетевые расходы на DNS, приветствия, и обмен сообщениями — это то, что доминирует в общем времени в предыдущих случаях — ответ сервера потребует только 20% общего ожидания! Но, по большому счету, имеют ли эти задержки значение? Если вы читаете это, то вы наверное уже знаете ответ — да, и очень даже большое.

Последние исследования пользователей рисуют следующую картину, что пользователи ожидают от любого интерфейса, как онлайн так и оффлайн приложений:

Задержка Реакция пользователя
0-100мс. мгновенно
100-300 мс. небольшая, но уже заметная задержка
300-1000мс. запрос в обработке
больше 1с. переключение внимания пользователя на другой контекст
больше 10с. я вернусь попозже

Таблица выше также объясняет неофициальное правило производительности в среде веб-приложений: отображайте ваши страницы, или, по крайней мере, предоставьте визуальный ответ на действие пользователя в течении 250мс для сохранения его заинтересованности вашим приложением. Но тут не просто скорость ради скорости. Исследования в Google, Amazon, Microsoft, как и многих тысяч других сайтов показывают, что дополнительная задержка имеет непосредственное влияние на успешность вашего сайта: более быстрые сайты приносят больше просмотров, выше лояльность пользователей и выше конверсия.

И так, что мы имеем — оптимальный показатель задержки около 250мс, но, однако, как мы видели выше, комбинация DNS запросов, установка TCP и SSL соединений, и доставка запросов отнимает до 370мс. Мы перешли лимит более чем на 50%, и мы все еще не учли время обработки запроса на сервере!

Для большинства пользователей и даже веб-разработчиков, DNS, TCP, SSL задержки совершенно непрозрачные, и формируются на слоях абстракции, о которых только немногие из нас думают. Тем не менее, каждый из этих этапов критически важен для взаимодействия с пользователем в целом, поскольку каждый дополнительной сетевой запрос может добавить десятки или сотни миллисекунд задержки.
Это та причина, по которой сетевой стек Хром есть много, много больше чем просто обработчик сокетов.

Проблему мы обсудили, пора переходить к деталям реализации.

P.S. переводчика: поскольку статья довольно большая, решил разбить ее на теорию и практику, другая часть интересней и намного большая. Как оказалось, очень много времени в обработке запроса на перевод занимает оформление под Хабр, около 40% времени, и вычитка на русском языке, поскольку для меня это в некоем роде двойной перевод. Спасибо за внимание.