В продолжение темы клиентской оптимизации слиянием ресурсов.
Как было показано раньше, выгодно разделить ресурсы на две группы: «ядро» (загружаются на всех страницах) и «ресурсы страницы» (загружаются только на страницах, которые их используют). Проблема состоит в выборе ресурсов, которые должны быть включены в ядро — слишком большое ядро может значительно увеличить время первоначальной загрузки сайта.
Есть три основных вида обращения к странице:
1. начальная загрузка (загружается и ядро, и ресурсы выбранной страницы)
2. загрузка новой страницы (ядро находится в кеше, загружаются ресурсы выбранной страницы)
3. загрузка старой страницы (и ядро, и ресурсы страницы находятся в кеше).
На скорость загрузки в третьем случае мы повлиять не можем. Кроме того, для ускорения начальной загрузки выгодно уменьшить объем ядра, поэтому оптимизация случая (1) ухудшает время загрузки в случае (2) и наоборот. Что делать?
формулы в PDF
Используем следующие обозначения:
Обозначим отношение количества случаев (1) к общему количеству случаев (1) и (2) как p_r (эту величину можно посчитать по логам вебсервера или использовать оценку 1 / n_p.
Обозначим вероятность выбора страницы i и время её загрузки как p_{1i} и t_{1i} в первом случае и p_{2i} и t_{2i} во втором.
(p_{1i} и p_{2i} можно посчитать по логам вебсервера). Получаем, что матожидание времени загрузки ресурсов произвольной страницы
M = \sum_i ( p_r t_{1i} p_{1i} + (1 — p_r) t_{2i} p_{2i} ).
В нашем случае t состоит из двух частей: сетевой задержки l (в которую мы включаем [почти] постоянные временные затраты, такие как время на установку соединения, передачу HTTP-запроса и получение заголовков ответа) и времени передачи данных d. При разбиении ресурсов на ядро (индекс c) и ресурсы страницы (индекс p) получаем, что
t_1i = l_{ci} + l_{pi} + d_{ci} + d_{pi},
t_2i = l_{pi} + d_{pi}.
Так как ресурсы ядра разделяются между всеми страницами
l_{ci} = l_{c} = const
d_{ci} = d_{c} — не зависит от страницы
Кроме того, l_{pi} = l_{p} = const, так как все ресурсы страницы объединяются вместе. Получаем, что
M = \sum_i ( p_r p_{1i} ( l_{c} + l_{p} + d_{c} + d_{pi} ) + ( 1 — p_r ) p_{2i} (l_{p} + d_{pi} ) ) =
= p_r (l_{c} + l_{p} ) + (1 — p_r) l_{p} +
+ p_r dd_{c} +
+ \sum_i ( p_r p_{1i} d_{pi}) + ( 1 — p_r ) p_{2i} d_{pi} )
Изменить l мы не можем, поэтому придётся минимизировать выражение
p_r d_{c} + \sum_i ( p_r p_{1i} d_{pi}) + ( 1 — p_r ) p_{2i} d_{pi}
Величина d, очевидно, пропорциональна размеру ресурса s (к счастью, ресурсы не влияют на скорость загрузки друг друга):
M ~ p_r s_{c} + \sum_i ( p_r p_{1i} s_{pi}) + ( 1 — p_r ) p_{2i} (s_{pi} )
Записывая выражение в матричном виде, получаем:
p_r 1_r^t S (1_r — a) + p_r p_1 B S a + ( 1 — p_r ) p_2 B S a =
= p_r 1_r^t S 1_r + (p_r p_1 B + ( 1 — p_r ) p_2 B — p_r 1_r^t) S a
Очевидно, что слагаемое p_r 1_r^t S 1_r постоянно. Таким образом, окончательное выражение, которые мы должны минимизировать:
(p_r p_1 B + ( 1 — p_r ) p_2 B — p_r 1_r^t) S a = c a
Величина (p_r p_1 B + ( 1 — p_r ) p_2 B — p_r 1_r^t) S может быть вычислена, так как она не содержит неизвестных. Результатом будет являться вектор-строка c. Так как a по определению может содержать только нули и единицы, задача становится тривиальной: a_i = 1, если c_i <= 0.
Как было показано раньше, выгодно разделить ресурсы на две группы: «ядро» (загружаются на всех страницах) и «ресурсы страницы» (загружаются только на страницах, которые их используют). Проблема состоит в выборе ресурсов, которые должны быть включены в ядро — слишком большое ядро может значительно увеличить время первоначальной загрузки сайта.
Есть три основных вида обращения к странице:
1. начальная загрузка (загружается и ядро, и ресурсы выбранной страницы)
2. загрузка новой страницы (ядро находится в кеше, загружаются ресурсы выбранной страницы)
3. загрузка старой страницы (и ядро, и ресурсы страницы находятся в кеше).
На скорость загрузки в третьем случае мы повлиять не можем. Кроме того, для ускорения начальной загрузки выгодно уменьшить объем ядра, поэтому оптимизация случая (1) ухудшает время загрузки в случае (2) и наоборот. Что делать?
формулы в PDF
Используем следующие обозначения:
- n_p — количество страниц;
- n_r — количество ресурсов;
- l — значение сетевой задержки;
- 1_r — вектор-столбец длиной n_r, содержащий только единицы;
- 1_p — вектор-строка длиной n_p, содержащий только единицы;
- d — время передачи данных;
- a — вектор-столбец длиной n_r. i-й элемент этого столбца соответствует i-му ресурсу. В том случае, если элемент равен нулю, ресурс включается в ядро, а в противном случае — в ресурсы страницы;
- B — матрица принадлежности ресурсов страницам. Cтолбцы соответствуют ресурсам, строки — страницам: b_{ij} = 1, если страница i использует ресурс j;
- S — диагональная матрица весов ресурсов (s_{ii} — вес ресурса i);
- p_1, p_2 — вектор-строка вероятности выбора страниц в первом и втором случае.
Обозначим отношение количества случаев (1) к общему количеству случаев (1) и (2) как p_r (эту величину можно посчитать по логам вебсервера или использовать оценку 1 / n_p.
Обозначим вероятность выбора страницы i и время её загрузки как p_{1i} и t_{1i} в первом случае и p_{2i} и t_{2i} во втором.
(p_{1i} и p_{2i} можно посчитать по логам вебсервера). Получаем, что матожидание времени загрузки ресурсов произвольной страницы
M = \sum_i ( p_r t_{1i} p_{1i} + (1 — p_r) t_{2i} p_{2i} ).
В нашем случае t состоит из двух частей: сетевой задержки l (в которую мы включаем [почти] постоянные временные затраты, такие как время на установку соединения, передачу HTTP-запроса и получение заголовков ответа) и времени передачи данных d. При разбиении ресурсов на ядро (индекс c) и ресурсы страницы (индекс p) получаем, что
t_1i = l_{ci} + l_{pi} + d_{ci} + d_{pi},
t_2i = l_{pi} + d_{pi}.
Так как ресурсы ядра разделяются между всеми страницами
l_{ci} = l_{c} = const
d_{ci} = d_{c} — не зависит от страницы
Кроме того, l_{pi} = l_{p} = const, так как все ресурсы страницы объединяются вместе. Получаем, что
M = \sum_i ( p_r p_{1i} ( l_{c} + l_{p} + d_{c} + d_{pi} ) + ( 1 — p_r ) p_{2i} (l_{p} + d_{pi} ) ) =
= p_r (l_{c} + l_{p} ) + (1 — p_r) l_{p} +
+ p_r dd_{c} +
+ \sum_i ( p_r p_{1i} d_{pi}) + ( 1 — p_r ) p_{2i} d_{pi} )
Изменить l мы не можем, поэтому придётся минимизировать выражение
p_r d_{c} + \sum_i ( p_r p_{1i} d_{pi}) + ( 1 — p_r ) p_{2i} d_{pi}
Величина d, очевидно, пропорциональна размеру ресурса s (к счастью, ресурсы не влияют на скорость загрузки друг друга):
M ~ p_r s_{c} + \sum_i ( p_r p_{1i} s_{pi}) + ( 1 — p_r ) p_{2i} (s_{pi} )
Записывая выражение в матричном виде, получаем:
p_r 1_r^t S (1_r — a) + p_r p_1 B S a + ( 1 — p_r ) p_2 B S a =
= p_r 1_r^t S 1_r + (p_r p_1 B + ( 1 — p_r ) p_2 B — p_r 1_r^t) S a
Очевидно, что слагаемое p_r 1_r^t S 1_r постоянно. Таким образом, окончательное выражение, которые мы должны минимизировать:
(p_r p_1 B + ( 1 — p_r ) p_2 B — p_r 1_r^t) S a = c a
Величина (p_r p_1 B + ( 1 — p_r ) p_2 B — p_r 1_r^t) S может быть вычислена, так как она не содержит неизвестных. Результатом будет являться вектор-строка c. Так как a по определению может содержать только нули и единицы, задача становится тривиальной: a_i = 1, если c_i <= 0.