Статьи

JLBH Примеры 2 — Учет скоординированного пропуска

В этом посте:

  • Запуск JLBH с учетом и без учета согласованного упущения
  • Пример численного воздействия скоординированного упущения
  • Дискуссия об управлении потоком

Вот пример, который я использовал при описании того, что было бы, если бы вы измеряли без учета скоординированного упущения:

Представим, что вы ждете поезд и задерживаетесь на станции на час, потому что поезд перед вами опаздывает. Тогда давайте представим, что вы садитесь в поезд на час позже, а поезду обычно требуется полчаса, чтобы добраться до пункта назначения. Если вы не учитываете согласованное упущение, вы не будете считать, что понесли какую-либо задержку, поскольку ваше путешествие заняло ровно столько времени, сколько вы ожидали на станции в течение часа перед отправлением!

Но это именно то, что вы делаете, когда запускаете микро-тест. Вы время каждого «путешествия», а не время ожидания.

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

По умолчанию измерения JLBH от начала до конца учитывают скоординированное упущение, хотя у вас есть настройка для его измерения без учета скоординированного пропуска.

Я написал этот простой тест, чтобы показать, насколько драматичным может быть эффект, связанный с согласованным упущением.

В этом примере после каждых 10 000 итераций мы добавляем задержку в миллисекунду:

01
02
03
04
05
06
07
08
09
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
package org.latency.spike;
 
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.jlbh.JLBH;
import net.openhft.chronicle.core.jlbh.JLBHOptions;
import net.openhft.chronicle.core.jlbh.JLBHTask;
 
/**
 * A simple JLBH example to show the effects od accounting for co-ordinated omission.
 * Toggle the accountForCoordinatedOmission to see results.
 */
public class SimpleSpikeJLBHTask implements JLBHTask {
    private int count = 0;
    private JLBH lth;
 
    public static void main(String[] args){
        JLBHOptions lth = new JLBHOptions()
                .warmUpIterations(40_000)
                .iterations(1_100_000)
                .throughput(100_000)
                .runs(3)
                .recordOSJitter(true)
                .accountForCoordinatedOmmission(true)
                .jlbhTask(new SimpleSpikeJLBHTask());
        new JLBH(lth).start();
    }
 
    @Override
    public void run(long startTimeNS) {
        if((count++)%10_000==0){
            //pause a while
            Jvm.busyWaitMicros(1000);
        }
        lth.sample(System.nanoTime() - startTimeNS);
    }
 
    @Override
    public void init(JLBH lth) {
        this.lth = lth;
    }
}

Если вы установите значение coordinatedOmission(false) вы получите этот профиль — как и ожидалось, задержка в миллисекундах может быть видна только на самом высоком процентиле, начиная с 99,99-го процентиля и выше. Или, так сказать, это влияет только на одну из каждых 10 итераций — не удивительно.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Warm up complete (40000 iterations took 0.046s)
-------------------------------- BENCHMARK RESULTS (RUN 1) -----------
Run time: 11.593s
Correcting for co-ordinated:false
Target throughput:100000/s = 1 message every 10us
End to End: (1,100,000)                         50/90 99/99.9 99.99/99.999 - worst was 0.11 / 0.13  0.20 / 0.33  999 / 999 - 1,930
OS Jitter (14,986)                              50/90 99/99.9 99.99 - worst was 8.4 / 15  68 / 1,080  3,210 - 4,330
----------------------------------------------------------------------
-------------------------------- BENCHMARK RESULTS (RUN 2) -----------
Run time: 11.49s
Correcting for co-ordinated:false
Target throughput:100000/s = 1 message every 10us
End to End: (1,100,000)                         50/90 99/99.9 99.99/99.999 - worst was 0.11 / 0.13  0.16 / 0.28  999 / 999 - 999
OS Jitter (13,181)                              50/90 99/99.9 99.99 - worst was 8.4 / 12  36 / 62  270 - 573
----------------------------------------------------------------------
-------------------------------- BENCHMARK RESULTS (RUN 3) -----------
Run time: 11.494s
Correcting for co-ordinated:false
Target throughput:100000/s = 1 message every 10us
End to End: (1,100,000)                         50/90 99/99.9 99.99/99.999 - worst was 0.11 / 0.13  0.16 / 0.26  999 / 999 - 1,030
OS Jitter (13,899)                              50/90 99/99.9 99.99 - worst was 8.4 / 13  42 / 76  160 - 541
----------------------------------------------------------------------
-------------------------------- SUMMARY (end to end)-----------------
Percentile   run1         run2         run3      % Variation
50:             0.11         0.11         0.11         0.00 
90:             0.13         0.13         0.13         0.00 
99:             0.20         0.16         0.16         3.31   
99.9:           0.33         0.28         0.26         3.88  
99.99:        999.42       999.42       999.42         0.00 
99.999:       999.42       999.42       999.42         0.00   
worst:       1933.31       999.42      1032.19         2.14  
 
----------------------------------------------------------------------

Но если вы установите значение coordinatedOmission(true) вы увидите истинный эффект этой задержки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Warm up complete (40000 iterations took 0.044s)
-------------------------------- BENCHMARK RESULTS (RUN 1) -----------
Run time: 11.0s
Correcting for co-ordinated:true
Target throughput:100000/s = 1 message every 10us
End to End: (1,100,000)                         50/90 99/99.9 99.99/99.999 - worst was 0.11 / 0.17  385 / 1,930  4,590 / 5,370 - 5,370
OS Jitter (13,605)                              50/90 99/99.9 99.99 - worst was 8.4 / 15  68 / 1,080  5,110 - 5,900
----------------------------------------------------------------------
-------------------------------- BENCHMARK RESULTS (RUN 2) -----------
Run time: 11.0s
Correcting for co-ordinated:true
Target throughput:100000/s = 1 message every 10us
End to End: (1,100,000)                         50/90 99/99.9 99.99/99.999 - worst was 0.12 / 0.18  42 / 901  999 / 999 - 1,030
OS Jitter (13,156)                              50/90 99/99.9 99.99 - worst was 8.4 / 13  38 / 68  209 - 467
----------------------------------------------------------------------
-------------------------------- BENCHMARK RESULTS (RUN 3) -----------
Run time: 11.0s
Correcting for co-ordinated:true
Target throughput:100000/s = 1 message every 10us
End to End: (1,100,000)                         50/90 99/99.9 99.99/99.999 - worst was 0.12 / 0.18  46 / 901  999 / 999 - 999
OS Jitter (13,890)                              50/90 99/99.9 99.99 - worst was 8.4 / 14  44 / 80  250 - 1,870
----------------------------------------------------------------------
-------------------------------- SUMMARY (end to end)-----------------
Percentile   run1         run2         run3      % Variation  
50:             0.11         0.12         0.12         0.00      
90:             0.17         0.18         0.18         0.00      
99:           385.02        41.98        46.08         6.11      
99.9:        1933.31       901.12       901.12         0.00      
99.99:       4587.52       999.42       999.42         0.00      
99.999:      5373.95       999.42       999.42         0.00      
worst:       5373.95      1032.19       999.42         2.14      
 
----------------------------------------------------------------------

На самом деле, одна из ста (не одна из 10 000) итераций затрагивается в некоторой степени. Вы также можете увидеть прогрессивный эффект латентности, когда вы возьмете процентили.

Это наглядно демонстрирует в цифрах, почему скоординированное упущение должно быть существенной частью вашего сравнительного анализа, особенно если вы не можете осуществлять управление потоком в своей программе. Контроль потока — это возможность перестать потреблять, если вы не успеваете, например, отталкивать пользователей от вашего сайта, если вы слишком заняты. Fix Engines не может управлять потоком, то есть вы не можете сказать рынку замедляться, потому что не можете идти в ногу! Программы, которые осуществляют управление потоком, ориентированы на потребителя, в то время как программы не обеспечивают управление потоком, ориентированы на производителя.

Учет скоординированного упущения идет рука об руку с возможностью установить задержку для определенной пропускной способности, что мы рассмотрим в следующем примере.