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

«Это было столь хорошо, что заставило меня изменить свое мнение..» — Эрих Шмидт, первоначально не желающий принимать идею Google Chrome.
Да, он смог! Сегодня Google Chrome является одним из наиболее популярных браузеров (35% доля рынка, показатели StatCounter) и доступен на Windows, Linux, OS X, Chrome OS, Android и iOS платформах. Несомненно, его достоинства и широкий функционал нашли отклик в сердцах пользователей, подарив много замечательных идей другим браузерам.
Оригинал 38 страничной книги комиксов, с разъяснениями идей и принципов Google Chrome предлагает нам замечательный пример мышления и процесса проектирования, результатом которых стал этот браузер. Однако, это только начало пути. Главные принципы, которые стали мотиваторами первых этапов разработки Chrome, нашли свое продолжение и в правилах непрерывного усовершенствования браузера:
- Скорость: сделать самый быстрый браузер
- Безопасность: предоставить пользователю наиболее защищенную среду для работы
- Стабильность: предоставить гибкую и стабильную платформу для веб-приложений
- Простота: сложные технологии за простым интерфейсом.
Как замечает команда разработки, много сайтов, которыми мы пользуемся сегодня — это уже не столько веб-страницы, сколько веб-приложения. В свою очередь, все большие и амбициозные приложения требуют скорости, безопасности и стабильности. Каждое из этих качеств заслуживает отдельной главы книги, но поскольку сегодня нашей темой является производительность, то и говорить главным образом будем о скорости.
Многогранность производительности.
Современные браузеры представляют собой платформу, которая во многом напоминает операционную систему, и Google Chrome не исключение. Браузеры, которые предшествовали Chrome, были спроектированы как монолитные программы, с одним рабочим процессом. Все открытые страницы делили между собой одно адресное пространство и работали с одними и теми же ресурсами, поэтому ошибка в обработке любой страницы или в механизме браузера, грозила сбоями и крахом всего приложения.

В отличие от этого подхода, в основе 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 сайтов интернета, имеет такие характеристики:

- «Вес» — 1280 КB
- Состоит из 88 ресурсов (картинки, css, js)
- Использует данные более чем из 30 сторонних сайтов.
Давайте посмотрим на это более детально. Свыше 1MB веса в среднем, состоит из 88 ресурсов, и собирается из 30 различных собственных и сторонних серверов. Заметим, что каждый из этих показателей неуклонно растет в течении нескольких последних лет, и нет повода прогнозировать, что этот рост остановится. Мы в все большем количестве строим все более громоздкие и требовательные веб-приложения, и этому не видно конца.
Сделав несложные математические расчеты на основе показателей HTTP Archive, можно увидеть, что средний ресурс страницы имеет вес 12KB (1045KB / 84), а это значит, что большинство интернет-соединений в браузере кратковременные и импульсивные . Это еще больше усложняет нам жизнь, поскольку лежащий в основе протокол (TCP) оптимизирован под большие, потоковые загрузки. Поэтому стоит докопаться до сути вещей, и рассмотреть один из типичных запросов на типичный ресурс.
Жизнь типичного запроса
Спецификация W3C Navigation Timing обеспечивает API браузера и возможность отследить часовые рамки и производительность каждого запроса. Давайте подробней рассмотрим его компоненты, так каждый из них представляет очень важную часть в общем впечатлении пользователя от производительности браузера.

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

Получив 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% времени, и вычитка на русском языке, поскольку для меня это в некоем роде двойной перевод. Спасибо за внимание.