forked from urfu-2016/markup-task-1
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
357 lines (333 loc) · 16 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Блог компании Яндекс</title>
</head>
<body>
<header>
<h1>Блог компании Яндекс.</h1>
</header>
<h2>ЯНДЕКС.ПОЧТА: КАК МЫ ИЗМЕРЯЕМ СКОРОСТЬ ЗАГРУЗКИ И УЛУЧШАЕМ ЕЁ</h2>
<article>
<p>
Если ваш сайт медленно грузится, вы рискуете тем, что люди не оценят ни то,
какой он красивый, ни то, какой он удобный. Никому не понравится, когда все
тормозит. Мы регулярно добавляем в Яндекс.Почту новую функциональность,
иногда — исправляем ошибки, а это значит, у нас постоянно появляются новый код
и новая логика. Всё это напрямую влияет на скорость работы интерфейса.
</p>
</article>
<article>
<h3>Что мы измеряем</h3>
<h4>Этапы первой загрузки:</h4>
<ul>
<li>подготовка;</li>
<li>загрузка статики (HTTP-запрос и парсинг);</li>
<li>исполнение модулей;</li>
<li>инициализация базовых объектов;</li>
<li>отрисовка.</li>
</ul>
<h4>Этапы отрисовки любой страницы:</h4>
<ul>
<li>подготовка к запросу на сервер;</li>
<li>запрос данных с сервера;</li>
<li>шаблонизация;</li>
<li>обновление DOM.</li>
</ul>
<p>— <q>Ок, теперь у нас есть метрики, мы можем отправить их на сервер</q> - говорим мы</p>
<p>— <q>Что же дальше?</q> - вопрошаете вы</p>
<p>— <q>А давай построим график!</q> - отвечаем мы</p>
<p>— <q>А что будем считать?</q> - уточняете вы</p>
<p>
Как вы знаете, медиана – это серединное, а не среднее значение в выборке.
Если у нас имеются числа 1, 2, 2, 3, 8, 10, 20, то медиана – 3, а среднее – 6,5.
В общем случае медиана отлично показывает, сколько грузится средний пользователь.
</p>
<p>
В случае ускорения или замедления медиана, конечно, изменится. Но она не может
рассказать, сколько пользователей ускорилось, а сколько замедлилось.
</p>
<p>
<abbr title="Application Performance Index — индекс производительности приложений">
APDEX
</abbr>
– метрика, которая сразу говорит: хорошо или плохо. Метрика
работает очень просто. Мы выбираем временной интервал [0; t], такой, что если
время показа страницы попало в него, то пользователь счастлив. Берем еще один
интервал, (t; 4t] (в четыре раза больше первого), и считаем, что если страница
показана за это время, то пользователь в целом удовлетворен скоростью работы,
но уже не настолько счастлив. И применяем формулу:
</p>
<p>
(кол-во счастливых пользователей + кол-во удовлетворенных / 2) / (кол-во всех).
</p>
<p>
Получается значение от нуля до единицы, которое, видимо, лучше всего показывает,
хорошо или плохо работает почта.
</p>
</article>
<article>
<h3>Как мы измеряем</h3>
<p>
Сейчас модуль обновления сам логирует все свои стадии, и можно легко понять
причину замедления: медленнее стал отвечать сервер либо слишком долго
выполняется JavaScript. Выглядит это примерно так:
</p>
<pre>
<code>
this.timings['look-ma-im-start'] = Date.now();
this.timings['look-ma-finish'] = Date.now();
</code>
</pre>
<p>
C помощью <code>Date.now()</code> мы получаем текущее время. Все тайминги собираются и при
отправке рассчитываются. На этапах разница между "end" и "start" не считается,
а все вычисления производятся в конце:
</p>
<pre>
<code>
var totalTime = this.timings['look-ma-finish'] - this.timings['look-ma-im-start'];
</code>
</pre>
<p>
И на сервер прилетают подобные записи:
</p>
<pre>
<code>
serverResponse=50&domUpdate=60
</code>
</pre>
</article>
<article>
<h3>Как мы ускоряем</h3>
<p>
Чтобы снизить время загрузки почты при выходе новых версий,
мы уже делаем следующее:
</p>
<ul>
<li>включаем gzip;</li>
<li>выставляем заголовки кэширования;</li>
<li>фризим CSS, JS, шаблоны и картинки;</li>
<li>используем CDN;</li>
</ul>
<p>
Мы подумали: <q>А что если хранить где-то старую версию файлов, а при выходе новой
передавать только diff между ней и той, которая сохранена у пользователя?</q>
В браузере же останется просто наложить патч на клиенте.
</p>
<p>
На самое деле эта идея не нова. Уже существуют стандарты для HTTP — например,
RFC 3229 <q>Delta encoding in HTTP</q> и <q>Google SDHC</q>, — но по разным причинам они
не получили должного распространения в браузерах и на серверах.
</p>
<p>
Мы же решили сделать свой аналог на JS. Чтобы реализовать этот метод обновления,
начали искать реализации diff на JS. На популярных хостингах кода нашли
библиотеки:
</p>
<ul>
<li>VCDiff</li>
<li>google-diff-patch-match</li>
</ul>
<p>
Для окончательного выбора библиотеки нам нужно сравнить:
</p>
<table>
<thead>
<tr>
<th>Библиотека</th>
<th>IE 9</th>
<th>Opera 12</th>
</tr>
</thead>
<tbody>
<tr>
<td>vcdiff</td>
<td>8</td>
<td>5</td>
</tr>
<tr>
<td>google diff</td>
<td>1363</td>
<td>76</td>
</tr>
</tbody>
</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>
Алгоритм Бройдена — Флетчера — Гольдфарба — Шанно (BFGS)
— итерационный метод численной оптимизации, предназначенный для
нахождения локального максимума/минимума нелинейного функционала
без ограничений.
</p>
<p>
Почему именно алгоритм Флетчера, а не другие популярные алгоритмы вроде:
</p>
<p>
<abbr title="Cyclic Redundancy Code">CRC16/32</abbr>
- алгоритм нахождения контрольной суммы, предназначенный для проверки
целостности данных
</p>
<p>
<abbr title="Message Digest 5">md5</abbr>
- 128-битный алгоритм хеширования.
Предназначен для создания «отпечатков»
или дайджестов сообщения произвольной длины и последующей проверки
их подлинности.
</p>
<p>
Потому что он быстрый, компактный и легок в реализации.
</p>
<blockquote>
дано ε, 𝓍<sub>0</sub><br>
инициализировать H<sub>0</sub><br>
𝓀 = 0 <br>
<code>while</code> ||∇𝒻<sub>𝓀</sub>||
> ε <br>
найти направление 𝓅<sub>𝓀</sub> = -C
<sub>𝓀</sub>
∇𝒻<sub>𝓀</sub> <br>
вычислить 𝓍<sub>𝓀+1</sub> = 𝓍<sub>𝓀</sub> +
α<sub>𝓀</sub>𝓅<sub>𝓀</sub>,<br>
α<sub>𝓀</sub> удовлетворяет условиям Вольфе<br>
обозначить 𝓈<sub>𝓀</sub>=𝓍<sub>𝓀+1</sub> - 𝓍<sub>𝓀</sub> и
𝓎<sub>𝓀</sub> = ∇𝒻<sub>𝓀+1</sub> - ∇𝒻<sub>𝓀</sub><br>
вычислить C<sub>𝓀+1</sub><br>
<code>end</code>
</blockquote>
</article>
<article>
<h3>Итог</h3>
<p>
Фактически мы экономим 80-90% трафика. Размер загружаемой статитки в байтах:
</p>
<table>
<thead>
<tr>
<th>Релиз</th>
<th>С патчем</th>
<th>Без патча</th>
</tr>
</thead>
<tbody>
<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>
</tbody>
</table>
</article>
<hr>
<div>
<p>Автор: <a href="">@doochik</a></p>
<p>С++ разработик</p>
<address>Электронная почта:
<a href="mailto:[email protected]">doochik@yandex-team.ru</a>
</address>
<p>Компания: <a href="http://yandex.ru/">Яндекс</a></p>
</div>
<hr>
<h2>Комментарии (3):</h2>
<article>
<h4>
Mogaika
(<a href="mailto:[email protected]">mogaika@yandex-team.ru</a>)
30 ноября 2014 в <time>17:05</time>
</h4>
<p>
А можете привести сравнение, на сколько быстрее грузится lite версия?
</p>
</article>
<hr>
<article>
<h4>
JIguse
(<a href="mailto:[email protected]">mrawesome@yandex.ru</a>)
29 ноября 2014 в <time>21:30</time>
</h4>
<p>
Спасибо за статью, познавательно. Здорово, что Яндекс делится некоторыми
подробностями о внутренней работе сервисов.
</p>
</article>
<hr>
<article>
<h4>
Brister
(<a href="mailto:[email protected]">brist89@andex-team.ru</a>)
24 ноября 2014 в <time>13:13</time>
</h4>
<p>
(кол-во счастливых пользователей + кол-во удовлетворенных / 2) / (кол-во всех).
</p>
<p>
Получается значение от нуля до единицы, которое, видимо, лучше всего показывает,
хорошо или плохо работает почта.
</p>
<p>
наверное все-таки от 0.5 до 1
</p>
</article>
<hr>
<article>
<h4>
alexeimois
(<a href="mailto:[email protected]">test@yandex.ru</a>)
22 ноября 2014 в <time>17:35</time>
</h4>
<p>
Мы измеряем скорость загрузки с помощью Яндекс.Метрики:
<a href="http://help.yandex.ru/metrika/reports/monitoring_timing.xml">
help.yandex.ru/metrika/reports/monitoring_timing.xml
</a>
</p>
</article>
<hr>
<footer>
<p>©Яндекс,</p>
<address><a href="mailto:[email protected]">help@yandex.ru</a>,</address>
<p>Хохрякова, 10</p>
</footer>
</body>
</html>