Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

252 changes: 251 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,258 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta charset="utf-8">
<title>Блог компании Яндекс.</title>
</head>
<body>
<header>
<h1>ЯНДЕКС.ПОЧТА: КАК МЫ ИЗМЕРЯЕМ СКОРОСТЬ ЗАГРУЗКИ И УЛУЧШАЕМ ЕЁ</h1>
</header>
<main>
<p>
<span>Если</span> ваш сайт медленно грузится, вы рискуете тем, что люди не оценят ни то,
какой он красивый, ни то, какой он удобный. Никому не понравится, когда все
тормозит. Мы регулярно добавляем в Яндекс.Почту новую функциональность,
иногда — исправляем ошибки, а это значит, у нас постоянно появляются новый код
и новая логика. Всё это напрямую влияет на скорость работы интерфейса.
</p>
<img src="http://windowsarena.ru/wp-content/uploads/2016/04/yandex_logo.png" alt="Яндекс">
<section>
<h3>Что мы измеряем</h3>
<p>Этапы первой загрузки:</p>
<ul>
<li>подготовка;</li>
<li>загрузка статики (HTTP-запрос и парсинг);</li>
<li>исполнение модулей;</li>
<li>инициализация базовых объектов;</li>
<li>отрисовка.</li>
</ul>
<p>Этапы отрисовки любой страницы:</p>
<ul>
<li>подготовка к запросу на сервер;</li>
<li>запрос данных с сервера;</li>
<li>шаблонизация;</li>
<li>обновление DOM.</li>
</ul>
<p>
— <q>Ок, теперь у нас есть метрики, мы можем отправить их на сервер</q> - говорим мы<br>
— <q>Что же дальше?</q> - вопрошаете вы<br>
— <q>А давай построим график!</q> - отвечаем мы<br>
— <q>А что будем считать?</q> - уточняете вы
</p>
<p>
Как вы знаете, <em>медиана</em> – это серединное, а не среднее значение в выборке.
Если у нас имеются числа 1, 2, 2, 3, 8, 10, 20, то медиана – 3, а среднее – 6,5.
В общем случае медиана отлично показывает, сколько грузится средний пользователь.
</p>
<p>
В случае ускорения или замедления медиана, конечно, изменится. Но она не может
рассказать, сколько пользователей ускорилось, а сколько замедлилось.
</p>
<p>
<dfn>APDEX</dfn> – метрика, которая сразу говорит: хорошо или плохо. Метрика
работает очень просто. Мы выбираем временной интервал [0; t], такой, что если
время показа страницы попало в него, то пользователь счастлив. Берем еще один
интервал, (t; 4t] (в четыре раза больше первого), и считаем, что если страница
показана за это время, то пользователь в целом удовлетворен скоростью работы,
но уже не настолько счастлив. И применяем формулу:
</p>
<p>
(кол-во счастливых пользователей + кол-во удовлетворенных / 2) / (кол-во всех).
</p>
<p>
Получается значение от нуля до единицы, которое, видимо, лучше всего показывает,
хорошо или плохо работает почта.
</p>
</section>
<section>
<h3>Как мы измеряем</h3>
<p>
Сейчас модуль обновления сам логирует все свои стадии, и можно легко понять
причину замедления: медленнее стал отвечать сервер либо слишком долго
выполняется JavaScript. Выглядит это примерно так:
</p>
<p>
this.timings['look-ma-im-start'] = Date.now();<br>
this.timings['look-ma-finish'] = Date.now();
</p>
<p>
C помощью Date.now() мы получаем текущее время. Все тайминги собираются и при
отправке рассчитываются. На этапах разница между “end” и “start” не считается,
а все вычисления производятся в конце:
</p>
<p>var totalTime = this.timings['look-ma-finish'] - this.timings['look-ma-im-start'];></p>
<p>И на сервер прилетают подобные записи:</p>
<p>serverResponse=50&amp;domUpdate=60</p>
</section>
<section>
<h3>Как мы ускоряем</h3>
<p>Чтобы снизить время загрузки почты при выходе новых версий,мы уже делаем следующее:</p>
<ol>
<li>включаем gzip;</li>
<li>выставляем заголовки кэширования;</li>
<li>фризим CSS, JS, шаблоны и картинки;</li>
<li>используем CDN;</li>
</ol>
<p>
Мы подумали: «А что если хранить где-то старую версию файлов, а при выходе новой
передавать только diff между ней и той, которая сохранена у пользователя?»
В браузере же останется просто наложить патч на клиенте.
</p>
<p>
На самое деле эта идея не нова. Уже существуют стандарты для HTTP — например,
RFC 3229 «Delta encoding in HTTP» и «Google SDHC», — но по разным причинам они
не получили должного распространения в браузерах и на серверах.
</p>
<p>
Мы же решили сделать свой аналог на JS. Чтобы реализовать этот метод обновления,
начали искать реализации diff на JS. На популярных хостингах кода нашли
библиотеки:
</p>
<p>
- VCDiff<br>
- google-diff-patch-match
</p>
<p>Для окончательного выбора библиотеки нам нужно сравнить:</p>
<table>
<tr>
<td>Библиотека</td>
<td>IE 9</td>
<td>Opera 12</td>
</tr>
<tr>
<td>vcdiff</td>
<td>8</td>
<td>5</td>
</tr>
<tr>
<td>google diff</td>
<td>1363</td>
<td>76</td>
</tr>
</table>
<p>
После того как мы определились с библиотекой для диффа, нужно определиться с тем,
где и как хранить статику на клиенте.
</p>
<p>Формат файла с патчами для проекта выглядит так:</p>
<pre>
<code>
[
{
"k": "jane.css",
"p": [patch],
"s": 4554
},
{
"k": "jane.css",
"p": [patch],
"s": 4554
}
]
</code>
</pre>
<p>
То есть это обычный массив из объектов. Каждый объект — отдельный ресурс. У
каждого объекта есть три свойства. k — названия ключа в localStorage для этого
ресурса. p — патч для ресурса, который сгенерировал vcdiff. s — чексумма для
ресурса актуальной версии, чтобы потом можно было проверить правильность
наложения патча на клиенте. Чексумма вычисляется по алгоритму Флетчера.
</p>
<p>
Алгоритм Бройдена — Флетчера — Гольдфарба — Шанн
(<abbr title="Broyden — Fletcher — Goldfarb — Shanno algorithm">BFGS</abbr>)
— итерационный метод численной оптимизации, предназначенный для
нахождения локального максимума/минимума нелинейного функционала
без ограничений.
</p>
<div id="BFGS">
дано &epsilon;, x<sub>0</sub><br>
инициализировать &Eta;<sub>0</sub><br>
k = 0<br>
while ||&nabla;&fnof;<sub>k</sub>|| &gt; &epsilon;<br>
&nbsp;&nbsp; найти направление
p<sub>k</sub> = &mdash;C<sub>k</sub>&nabla; &fnof;<sub>k</sub><br>
&nbsp;&nbsp; вычислить
x<sub>k+1</sub> = x<sub>k</sub> + &alpha;<sub>k</sub> p<sub>k</sub>,
&alpha;<sub>k</sub> удовлетворяет условиям Вольфе<br>
&nbsp;&nbsp; обозначить
s<sub>k</sub> = x<sub>k+1</sub> &mdash; x<sub>k</sub> и
y<sub>k</sub> = &nabla; &fnof;<sub>k+1</sub> &mdash; &nabla; &fnof;<sub>k</sub><br>
&nbsp;&nbsp; вычислить
C<sub>k+1</sub><br>
&nbsp;&nbsp; k = k + 1<br>
end
</div>
<p>Почему именно алгоритм Флетчера, а не другие популярные алгоритмы вроде:</p>
<p>
CRC16/32 - алгоритм нахождения контрольной суммы,
предназначенный для проверки целостности данных
</p>
<p>
md5 - 128-битный алгоритм хеширования. Предназначен для создания «отпечатков»
или дайджестов сообщения произвольной длины и последующей проверки
их подлинности.
</p>
<p>Потому что он быстрый, компактный и легок в реализации.</p>
</section>
<section>
<h3>Итог</h3>
<p>Фактически мы экономим 80-90% трафика. Размер загружаемой статитки в байтах:</p>
<table>
<tr>
<td>Релиз</td>
<td>С патчем</td>
<td>Без патча</td>
</tr>
<tr>
<td>7.7.20</td>
<td>397</td>
<td>174 549</td>
</tr>
<tr>
<td>7.7.21</td>
<td>383</td>
<td>53 995</td>
</tr>
<tr>
<td>7.7.22</td>
<td>483</td>
<td>3 995</td>
</tr>
</table>
<p>
Автор: @doochik<br>
С++ разработик<br>
Электронная почта: (doochik@yandex-team.ru)<br>
Компания: Яндекс
</p>
<p>Комментарии (3):</p>
<p>Mogaika (mogaika@yandex-team.ru) 30 ноября 2014 в 17:05</p>
<p>А можете привести сравнение, на сколько быстрее грузится lite версия?</p>
<p>JIguse (mrawesome@yandex.ru) 29 ноября 2014 в 21:30</p>
<p>
Спасибо за статью, познавательно. Здорово, что Яндекс делится некоторыми
подробностями о внутренней работе сервисов.
</p>
<p>Brister (brist89@yandex-team.ru) 24 ноября 2014 в 13:13</p>
<p>
(кол-во счастливых пользователей + кол-во удовлетворенных / 2) / (кол-во всех).
Получается значение от нуля до единицы, которое, видимо, лучше всего показывает,
хорошо или плохо работает почта.
</p>
<p>наверное все-таки от 0.5 до 1</p>
<p>alexeimois (test@yandex.ru) 22 ноября 2014 в 17:35</p>
<p>
Мы измеряем скорость загрузки с помощью Яндекс.Метрики:
<a href="https://help.yandex.ru/metrika/reports/monitoring_timing.xml">
help.yandex.ru/metrika/reports/monitoring_timing.xml
</a>
</p>
</section>
</main>
<footer>
<address>&copy; Яндекс, help@yandex.ru, Хохрякова, 10</address>
</footer>
</body>
</html>