SlideShare a Scribd company logo
1 of 166
Download to read offline
Теория и практика .NET-бенчмаркинга
Андрей Акиньшин, JetBrains
Екатеринбург, 02.11.2016
1/85
Часть 1
Теория
2/85 1. Теория
Часть 1.1
Почему мы об этом говорим?
3/85 1.1 Теория: Почему мы об этом говорим?
StackOverflow
Люди любят бенчмаркать
4/85 1.1 Теория: Почему мы об этом говорим?
StackOverflow
Типичный вопрос
5/85 1.1 Теория: Почему мы об этом говорим?
Habrahabr
Некоторые делают выводы и пишут статьи
6/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
• Сравнение алгоритмов
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
• Сравнение алгоритмов
• Оценка улучшений производительности
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
• Сравнение алгоритмов
• Оценка улучшений производительности
• Анализ регрессии
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
• Сравнение алгоритмов
• Оценка улучшений производительности
• Анализ регрессии
• . . .
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
• Сравнение алгоритмов
• Оценка улучшений производительности
• Анализ регрессии
• . . .
• Научный интерес
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
• Сравнение алгоритмов
• Оценка улучшений производительности
• Анализ регрессии
• . . .
• Научный интерес
• Маркетинг
7/85 1.1 Теория: Почему мы об этом говорим?
Применения бенчмарков
• Performance analysis
• Сравнение алгоритмов
• Оценка улучшений производительности
• Анализ регрессии
• . . .
• Научный интерес
• Маркетинг
• Весёлое времяпрепровождение
7/85 1.1 Теория: Почему мы об этом говорим?
Часть 1.2
Общая методология
8/85 1.2 Теория: Общая методология
План performance-работ
9/85 1.2 Теория: Общая методология
План performance-работ
1 Поставить задачу
9/85 1.2 Теория: Общая методология
План performance-работ
1 Поставить задачу
2 Выбрать метрики
9/85 1.2 Теория: Общая методология
План performance-работ
1 Поставить задачу
2 Выбрать метрики
3 Выбрать инструмент
9/85 1.2 Теория: Общая методология
План performance-работ
1 Поставить задачу
2 Выбрать метрики
3 Выбрать инструмент
4 Провести эксперимент
9/85 1.2 Теория: Общая методология
План performance-работ
1 Поставить задачу
2 Выбрать метрики
3 Выбрать инструмент
4 Провести эксперимент
5 Получить результаты
9/85 1.2 Теория: Общая методология
План performance-работ
1 Поставить задачу
2 Выбрать метрики
3 Выбрать инструмент
4 Провести эксперимент
5 Получить результаты
6 Выполнить анализ и сделать выводы
9/85 1.2 Теория: Общая методология
План performance-работ
1 Поставить задачу
2 Выбрать метрики
3 Выбрать инструмент
4 Провести эксперимент
5 Получить результаты
6 Выполнить анализ и сделать выводы
Анализ полученных данных — самый важный этап
9/85 1.2 Теория: Общая методология
Виды performance-работ
10/85 1.2 Теория: Общая методология
Виды performance-работ
• Profiling
10/85 1.2 Теория: Общая методология
Виды performance-работ
• Profiling
• Monitoring
10/85 1.2 Теория: Общая методология
Виды performance-работ
• Profiling
• Monitoring
• Performance tests
10/85 1.2 Теория: Общая методология
Виды performance-работ
• Profiling
• Monitoring
• Performance tests
• Benchmarking (micro/macro)
10/85 1.2 Теория: Общая методология
Виды performance-работ
• Profiling
• Monitoring
• Performance tests
• Benchmarking (micro/macro)
• . . .
10/85 1.2 Теория: Общая методология
Performance spaces
11/85 1.2 Теория: Общая методология
Performance spaces
Исходный код
11/85 1.2 Теория: Общая методология
Performance spaces
Исходный код
X
Окружение
11/85 1.2 Теория: Общая методология
Performance spaces
Исходный код
X
Окружение
X
Входные данные
11/85 1.2 Теория: Общая методология
Performance spaces
Исходный код
X
Окружение
X
Входные данные
⇓
Распределение
11/85 1.2 Теория: Общая методология
Performance spaces
Непонимание проблематики влечёт за собой
следующие проблемы:
12/85 1.2 Теория: Общая методология
Performance spaces
Непонимание проблематики влечёт за собой
следующие проблемы:
• Легко обмануть себя, сделать неправильные
выводы, принять вредные бизнес-решения
12/85 1.2 Теория: Общая методология
Performance spaces
Непонимание проблематики влечёт за собой
следующие проблемы:
• Легко обмануть себя, сделать неправильные
выводы, принять вредные бизнес-решения
• Легко пропустить важную конфигурацию,
которая испортит жизнь в продакшене
12/85 1.2 Теория: Общая методология
Performance spaces
Непонимание проблематики влечёт за собой
следующие проблемы:
• Легко обмануть себя, сделать неправильные
выводы, принять вредные бизнес-решения
• Легко пропустить важную конфигурацию,
которая испортит жизнь в продакшене
• Легко повестись на кривые бенчмарки или
чёрный маркетинг
12/85 1.2 Теория: Общая методология
Окружение
C# compiler старый csc / Roslyn
CLR CLR2 / CLR4 / CoreCLR / Mono
OS Windows / Linux / MacOS / FreeBSD
JIT LegacyJIT-x86 / LegacyJIT-x64 / RyuJIT-x64
GC MS (разные CLR) / Mono (Boehm/Sgen)
Toolchain JIT / NGen / .NET Native
Hardware тысячи его
. . . . . .
13/85 1.2 Теория: Общая методология
Окружение
C# compiler старый csc / Roslyn
CLR CLR2 / CLR4 / CoreCLR / Mono
OS Windows / Linux / MacOS / FreeBSD
JIT LegacyJIT-x86 / LegacyJIT-x64 / RyuJIT-x64
GC MS (разные CLR) / Mono (Boehm/Sgen)
Toolchain JIT / NGen / .NET Native
Hardware тысячи его
. . . . . .
И не забываем про версии, много-много версий
13/85 1.2 Теория: Общая методология
Окружение и запуск
Советы по запуску бенчмарков
14/85 1.2 Теория: Общая методология
Окружение и запуск
Советы по запуску бенчмарков
• Release build
14/85 1.2 Теория: Общая методология
Окружение и запуск
Советы по запуску бенчмарков
• Release build
• Без дебаггера
14/85 1.2 Теория: Общая методология
Окружение и запуск
Советы по запуску бенчмарков
• Release build
• Без дебаггера
• Выключите другие приложения
14/85 1.2 Теория: Общая методология
Окружение и запуск
Советы по запуску бенчмарков
• Release build
• Без дебаггера
• Выключите другие приложения
• Используйте максимальную
производительность
14/85 1.2 Теория: Общая методология
Требования к бенчмарку
15/85 1.2 Теория: Общая методология
Требования к бенчмарку
• Надёжность
15/85 1.2 Теория: Общая методология
Требования к бенчмарку
• Надёжность
• Точность
15/85 1.2 Теория: Общая методология
Требования к бенчмарку
• Надёжность
• Точность
• Воспроизводимость
15/85 1.2 Теория: Общая методология
Требования к бенчмарку
• Надёжность
• Точность
• Воспроизводимость
• Изолированность
15/85 1.2 Теория: Общая методология
Требования к бенчмарку
• Надёжность
• Точность
• Воспроизводимость
• Изолированность
• Переносимость
15/85 1.2 Теория: Общая методология
Требования к бенчмарку
• Надёжность
• Точность
• Воспроизводимость
• Изолированность
• Переносимость
• Простота
15/85 1.2 Теория: Общая методология
Требования к бенчмарку
• Надёжность
• Точность
• Воспроизводимость
• Изолированность
• Переносимость
• Простота
• Честность
15/85 1.2 Теория: Общая методология
BenchmarkDotNet
16/85 1.2 Теория: Общая методология
Часть 1.3
Таймеры
17/85 1.3 Теория: Таймеры
DateTime vs Stopwatch
var start = DateTime.Now;
Foo();
var finish = DateTime.Now;
var time = (finish - start).TotalMilliseconds;
vs
var sw = Stopwatch.StartNew();
Foo();
sw.Stop();
var time = sw.ElapsedMilliseconds;
18/85 1.3 Теория: Таймеры
Характеристики таймеров
19/85 1.3 Теория: Таймеры
Характеристики таймеров
• Монотонность
замеры должны неуменьшаться
19/85 1.3 Теория: Таймеры
Характеристики таймеров
• Монотонность
замеры должны неуменьшаться
• Resolution
минимальное положительное время между
замерами
19/85 1.3 Теория: Таймеры
Характеристики таймеров
• Монотонность
замеры должны неуменьшаться
• Resolution
минимальное положительное время между
замерами
• Latency
время на получение замера
19/85 1.3 Теория: Таймеры
DateTime.UtcNow
20/85 1.3 Теория: Таймеры
DateTime.UtcNow
OS Implementation
Windows GetSystemTimeAsFileTime
Linux gettimeofday
20/85 1.3 Теория: Таймеры
DateTime.UtcNow
OS Implementation
Windows GetSystemTimeAsFileTime
Linux gettimeofday
OS Runtime Time∗
Latency Windows Full/Core ≈7–8ns
Latency Windows Mono ≈30–31ns
Resolution Windows Any ≈0.5..15.625ms
20/85 1.3 Теория: Таймеры
DateTime.UtcNow
OS Implementation
Windows GetSystemTimeAsFileTime
Linux gettimeofday
OS Runtime Time∗
Latency Windows Full/Core ≈7–8ns
Latency Windows Mono ≈30–31ns
Resolution Windows Any ≈0.5..15.625ms
Latency Linux Mono ≈26–30ns
Resolution Linux Mono ≈1µs
20/85 1.3 Теория: Таймеры
DateTime.UtcNow
OS Implementation
Windows GetSystemTimeAsFileTime
Linux gettimeofday
OS Runtime Time∗
Latency Windows Full/Core ≈7–8ns
Latency Windows Mono ≈30–31ns
Resolution Windows Any ≈0.5..15.625ms
Latency Linux Mono ≈26–30ns
Resolution Linux Mono ≈1µs
∗
Intel i7-4702MQ CPU 2.20GHz
См. также: http://aakinshin.net/en/blog/dotnet/datetime/
20/85 1.3 Теория: Таймеры
Stopwatch.GetTimestamp()
21/85 1.3 Теория: Таймеры
Stopwatch.GetTimestamp()
Hardware timers
• NA
• TSC (Variant / Constant / Invariant)
• ACPI PM (Freq = 3.579545 MHz)
• HPET (Freq = 14.31818 MHz)
21/85 1.3 Теория: Таймеры
Stopwatch.GetTimestamp()
Hardware timers
• NA
• TSC (Variant / Constant / Invariant)
• ACPI PM (Freq = 3.579545 MHz)
• HPET (Freq = 14.31818 MHz)
Implementation
• Windows: QueryPerformanceCounter
• Linux: clock_gettime /
mach_absolute_time / gettimeofday
21/85 1.3 Теория: Таймеры
Stopwatch.GetTimestamp()
Runtime OS Timer 1 tick Latency Resolution
Full Win TSC 300-400ns 15-18ns 300-400ns
Full Win HPET 69.8ns 500-800ns ≈Latency
Full Win NA 100ns 7-10ns 0.5-55ms
Mono Win TSC 100ns 35-45ns 300-400ns
Mono Win HPET 100ns 500-800ns ≈Latency
Mono Win NA 100ns 30-40ns 0.5-55ms
Core Linux TSC 1ns 30-35ns ≈Latency
Core Linux HPET/ACPI 1ns 500-800ns ≈Latency
Mono Linux TSC 100ns 20-25ns 100ns
Mono Linux HPET/ACPI 100ns 500-800ns ≈Latency
Intel i7-4702MQ CPU 2.20GHz
См. также: http://aakinshin.net/en/blog/dotnet/stopwatch/
22/85 1.3 Теория: Таймеры
Важно помнить про таймеры
23/85 1.3 Теория: Таймеры
Важно помнить про таймеры
• Важно понимать значения Latency и Resolution
23/85 1.3 Теория: Таймеры
Важно помнить про таймеры
• Важно понимать значения Latency и Resolution
• 1 tick ≠ Resolution
23/85 1.3 Теория: Таймеры
Важно помнить про таймеры
• Важно понимать значения Latency и Resolution
• 1 tick ≠ Resolution
• Время может идти назад
23/85 1.3 Теория: Таймеры
Важно помнить про таймеры
• Важно понимать значения Latency и Resolution
• 1 tick ≠ Resolution
• Время может идти назад
• Два последовательных замера могут быть равны
23/85 1.3 Теория: Таймеры
Важно помнить про таймеры
• Важно понимать значения Latency и Resolution
• 1 tick ≠ Resolution
• Время может идти назад
• Два последовательных замера могут быть равны
• Два последовательных замера могут различаться на
миллисекунды
23/85 1.3 Теория: Таймеры
Часть 1.4
Количество итераций
24/85 1.4 Теория: Количество итераций
Микробенчмаркинг
Плохой бенчмарк
// Resolution(Stopwatch) = 466 ns
// Latency(Stopwatch) = 18 ns
var sw = Stopwatch.StartNew();
Foo(); // 100 ns
sw.Stop();
WriteLine(sw.ElapsedMilliseconds);
25/85 1.4 Теория: Количество итераций
Микробенчмаркинг
Плохой бенчмарк
// Resolution(Stopwatch) = 466 ns
// Latency(Stopwatch) = 18 ns
var sw = Stopwatch.StartNew();
Foo(); // 100 ns
sw.Stop();
WriteLine(sw.ElapsedMilliseconds);
Небольшое улучшение
var sw = Stopwatch.StartNew();
for (int i = 0; i < N; i++) // (N * 100 + eps) ns
Foo();
sw.Stop();
var total = sw.ElapsedTicks / Stopwatch.Frequency;
WriteLine(total / N);
25/85 1.4 Теория: Количество итераций
Прогрев
Запустим бенчмарк несколько раз:
int[] x = new int[128 * 1024 * 1024];
for (int iter = 0; iter < 5; iter++)
{
var sw = Stopwatch.StartNew();
for (int i = 0; i < x.Length; i += 16)
x[i]++;
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
26/85 1.4 Теория: Количество итераций
Прогрев
Запустим бенчмарк несколько раз:
int[] x = new int[128 * 1024 * 1024];
for (int iter = 0; iter < 5; iter++)
{
var sw = Stopwatch.StartNew();
for (int i = 0; i < x.Length; i += 16)
x[i]++;
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
Результат:
176
81
62
62
62
26/85 1.4 Теория: Количество итераций
Несколько запусков метода
Run 01 : 529.8674 ns/op
Run 02 : 532.7541 ns/op
Run 03 : 558.7448 ns/op
Run 04 : 555.6647 ns/op
Run 05 : 539.6401 ns/op
Run 06 : 539.3494 ns/op
Run 07 : 564.3222 ns/op
Run 08 : 551.9544 ns/op
Run 09 : 550.1608 ns/op
Run 10 : 533.0634 ns/op
27/85 1.4 Теория: Количество итераций
Несколько запусков бенчмарка
28/85 1.4 Теория: Количество итераций
Простой случай
Центральная предельная теорема
спешит на помощь!
29/85 1.4 Теория: Количество итераций
Но есть и сложные случаи
30/85 1.4 Теория: Количество итераций
Часть 1.5
Различные сложности
31/85 1.5 Теория: Различные сложности
Накладные расходы
var sw = Stopwatch.StartNew();
int x = 0;
for (int i = 0; i < N; i++) // overhead
x++; // target operation
sw.Stop();
32/85 1.5 Теория: Различные сложности
Изоляция бенчмарков
Плохой бенчмарк
var sw1 = Stopwatch.StartNew();
Foo();
sw1.Stop();
var sw2 = Stopwatch.StartNew();
Bar();
sw2.Stop();
33/85 1.5 Теория: Различные сложности
Изоляция бенчмарков
Плохой бенчмарк
var sw1 = Stopwatch.StartNew();
Foo();
sw1.Stop();
var sw2 = Stopwatch.StartNew();
Bar();
sw2.Stop();
Вспомним про:
• Interface method dispatch
• Garbage collector and autotuning
• Jitting
33/85 1.5 Теория: Различные сложности
Борьба с оптимизациями
34/85 1.5 Теория: Различные сложности
Борьба с оптимизациями
• Dead code elimination
34/85 1.5 Теория: Различные сложности
Борьба с оптимизациями
• Dead code elimination
• Inlining
34/85 1.5 Теория: Различные сложности
Борьба с оптимизациями
• Dead code elimination
• Inlining
• Constant folding
34/85 1.5 Теория: Различные сложности
Борьба с оптимизациями
• Dead code elimination
• Inlining
• Constant folding
• Instruction Level Parallelism
34/85 1.5 Теория: Различные сложности
Борьба с оптимизациями
• Dead code elimination
• Inlining
• Constant folding
• Instruction Level Parallelism
• Branch prediction
34/85 1.5 Теория: Различные сложности
Борьба с оптимизациями
• Dead code elimination
• Inlining
• Constant folding
• Instruction Level Parallelism
• Branch prediction
• . . .
34/85 1.5 Теория: Различные сложности
Доступ к памяти
Event Latency Scaled
1 CPU cycle 0.3 ns 1 s
Level 1 cache access 0.9 ns 3 s
Level 2 cache access 2.8 ns 9 s
Level 3 cache access 12.9 ns 43 s
Main memory access 120 ns 6 min
Solid-state disk I/O 50-150 µs 2-6 days
Rotational disk I/O 1-10 ms 1-12 months
Internet: SF to NYC 40 ms 4 years
Internet: SF to UK 81 ms 8 years
Internet: SF to Australia 183 ms 19 years
OS virtualization reboot 4 s 423 years
SCSI command time-out 30 s 3000 years
Hardware virtualization reboot 40 s 4000 years
Physical system reboot 5 m 32 millenia
© Systems Performance: Enterprise and the Cloud
35/85 1.5 Теория: Различные сложности
Processor affinity
36/85 1.5 Теория: Различные сложности
True sharing
37/85 1.5 Теория: Различные сложности
False sharing
38/85 1.5 Теория: Различные сложности
False sharing в действии
private static int[] x = new int[1024];
private void Inc(int p)
{
for (int i = 0; i < 10000001; i++)
x[p]++;
}
private void Run(int step)
{
var sw = Stopwatch.StartNew();
Task.WaitAll(
Task.Factory.StartNew(() => Inc(0 * step)),
Task.Factory.StartNew(() => Inc(1 * step)),
Task.Factory.StartNew(() => Inc(2 * step)),
Task.Factory.StartNew(() => Inc(3 * step)));
Console.WriteLine(sw.ElapsedMilliseconds);
}
39/85 1.5 Теория: Различные сложности
False sharing в действии
private static int[] x = new int[1024];
private void Inc(int p)
{
for (int i = 0; i < 10000001; i++)
x[p]++;
}
private void Run(int step)
{
var sw = Stopwatch.StartNew();
Task.WaitAll(
Task.Factory.StartNew(() => Inc(0 * step)),
Task.Factory.StartNew(() => Inc(1 * step)),
Task.Factory.StartNew(() => Inc(2 * step)),
Task.Factory.StartNew(() => Inc(3 * step)));
Console.WriteLine(sw.ElapsedMilliseconds);
}
Run(1) Run(256)
≈400ms ≈150ms
39/85 1.5 Теория: Различные сложности
Бенчмаркинг — это сложно
Anon et al., “A Measure of Transaction Processing Power”
There are lies, damn lies and then there are performance
measures.
40/85 1.5 Теория: Различные сложности
Часть 2
Практика
41/85 2. Практика
Часть 2.1
Сложности нанобенчмаркинга
42/85 2.1 Практика: Сложности нанобенчмаркинга
Задача
Какой из методов работает быстрее?
[MethodImpl(MethodImplOptions.NoInlining)]
public void Empty0() {}
[MethodImpl(MethodImplOptions.NoInlining)]
public void Empty1() {}
[MethodImpl(MethodImplOptions.NoInlining)]
public void Empty2() {}
[MethodImpl(MethodImplOptions.NoInlining)]
public void Empty3() {}
43/85 2.1 Практика: Сложности нанобенчмаркинга
Попытка решения
Давайте забенчмаркаем!
private void MeasureX() // X = 0, 1, 2, 3
{
for (int i = 0; i < Rep; i++)
{
var sw = Stopwatch.StartNew();
for (int j = 0; j < N; j++)
EmptyX(); // X = 0, 1, 2, 3
sw.Stop();
Write(sw.ElapsedMilliseconds + " ");
}
}
44/85 2.1 Практика: Сложности нанобенчмаркинга
Попытка решения
Давайте забенчмаркаем!
private void MeasureX() // X = 0, 1, 2, 3
{
for (int i = 0; i < Rep; i++)
{
var sw = Stopwatch.StartNew();
for (int j = 0; j < N; j++)
EmptyX(); // X = 0, 1, 2, 3
sw.Stop();
Write(sw.ElapsedMilliseconds + " ");
}
}
Empty0: 242 253 245 253 242 244 245 255 245 245 // Slow
Empty1: 224 228 229 224 223 224 227 222 228 222 // Fast
Empty2: 229 222 226 222 224 226 227 229 225 230 // Fast
Empty3: 241 240 237 244 242 241 238 245 239 239 // Slow
Intel Core i7 Haswell, RyuJIT-x64
44/85 2.1 Практика: Сложности нанобенчмаркинга
Обратимся к классике
Рекомендуемая литература:
Agner Fog,
“The microarchitecture of Intel, AMD and VIA CPUs.
An optimization guide for assembly programmers and
compiler makers.”
45/85 2.1 Практика: Сложности нанобенчмаркинга
Обратимся к классике
Рекомендуемая литература:
Agner Fog,
“The microarchitecture of Intel, AMD and VIA CPUs.
An optimization guide for assembly programmers and
compiler makers.”
3.8 Branch prediction in Intel Haswell, Broadwell and Skylake
Pattern recognition for indirect jumps and calls.
Indirect jumps and indirect calls are predicted well.
45/85 2.1 Практика: Сложности нанобенчмаркинга
Воспользуемся верным инструментом
46/85 2.1 Практика: Сложности нанобенчмаркинга
Старая добрая раскрутка цикла
var sw = Stopwatch.StartNew();
for (int j = 0; j < N; j++)
{
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
Empty0();
}
sw.Stop();
47/85 2.1 Практика: Сложности нанобенчмаркинга
Раскрутим наш цикл
Есть ли статистически значимая разница между замерами?
48/85 2.1 Практика: Сложности нанобенчмаркинга
Работа над ошибками
• Выключайте приложения во время
бенчмаркинга!
Результаты без запущенной VisualStudio:
• Не пытайтесь оценивать статистическую
значимость на глаз.
49/85 2.1 Практика: Сложности нанобенчмаркинга
Часть 2.2
Работаем с памятью
50/85 2.2 Практика: Работаем с памятью
Сумма элементов массива
const int N = 1024;
int[,] a = new int[N, N];
[Benchmark]
public double SumIj()
{
var sum = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
sum += a[i, j];
return sum;
}
[Benchmark]
public double SumJi()
{
var sum = 0;
for (int j = 0; j < N; j++)
for (int i = 0; i < N; i++)
sum += a[i, j];
return sum;
}
51/85 2.2 Практика: Работаем с памятью
Сумма элементов массива
const int N = 1024;
int[,] a = new int[N, N];
[Benchmark]
public double SumIj()
{
var sum = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
sum += a[i, j];
return sum;
}
[Benchmark]
public double SumJi()
{
var sum = 0;
for (int j = 0; j < N; j++)
for (int i = 0; i < N; i++)
sum += a[i, j];
return sum;
}
SumIj SumJi
LegacyJIT-x86 1 попугай 3.5 попугая
51/85 2.2 Практика: Работаем с памятью
Часть 2.3
Работаем с условными
переходами
52/85 2.3 Практика: Работаем с условными переходами
Branch prediction
const int N = 32767;
int[] sorted, unsorted; // random numbers [0..255]
private static int Sum(int[] data)
{
int sum = 0;
for (int i = 0; i < N; i++)
if (data[i] >= 128)
sum += data[i];
return sum;
}
[Benchmark]
public int Sorted()
{
return Sum(sorted);
}
[Benchmark]
public int Unsorted()
{
return Sum(unsorted);
}
53/85 2.3 Практика: Работаем с условными переходами
Branch prediction
const int N = 32767;
int[] sorted, unsorted; // random numbers [0..255]
private static int Sum(int[] data)
{
int sum = 0;
for (int i = 0; i < N; i++)
if (data[i] >= 128)
sum += data[i];
return sum;
}
[Benchmark]
public int Sorted()
{
return Sum(sorted);
}
[Benchmark]
public int Unsorted()
{
return Sum(unsorted);
}
Sorted Unsorted
LegacyJIT-x86 1 попугай 7.4 попугая
53/85 2.3 Практика: Работаем с условными переходами
Часть 2.4
Interface method dispatch
54/85 2.4 Практика: Interface method dispatch
Интерфейсы
private interface IFoo {
double Inc(double x);
}
private class Foo1 : IFoo {
public double Inc(double x) =>
x + 1;
}
private class Foo2 : IFoo {
public double Inc(double x) =>
x + 1;
}
private double Run(IFoo foo) {
double sum = 0;
for (int i = 0; i < 1001; i++)
sum += foo.Inc(0);
return sum;
}
55/85 2.4 Практика: Interface method dispatch
Интерфейсы
private interface IFoo {
double Inc(double x);
}
private class Foo1 : IFoo {
public double Inc(double x) =>
x + 1;
}
private class Foo2 : IFoo {
public double Inc(double x) =>
x + 1;
}
private double Run(IFoo foo) {
double sum = 0;
for (int i = 0; i < 1001; i++)
sum += foo.Inc(0);
return sum;
}
[Benchmark]
public double Run11() {
var bar1 = new Foo1();
var bar2 = new Foo1();
return Run(bar1) + Run(bar2);
}
[Benchmark]
public double Run12() {
var bar1 = new Foo1();
var bar2 = new Foo2();
return Run(bar1) + Run(bar2);
}
55/85 2.4 Практика: Interface method dispatch
Интерфейсы
private interface IFoo {
double Inc(double x);
}
private class Foo1 : IFoo {
public double Inc(double x) =>
x + 1;
}
private class Foo2 : IFoo {
public double Inc(double x) =>
x + 1;
}
private double Run(IFoo foo) {
double sum = 0;
for (int i = 0; i < 1001; i++)
sum += foo.Inc(0);
return sum;
}
[Benchmark]
public double Run11() {
var bar1 = new Foo1();
var bar2 = new Foo1();
return Run(bar1) + Run(bar2);
}
[Benchmark]
public double Run12() {
var bar1 = new Foo1();
var bar2 = new Foo2();
return Run(bar1) + Run(bar2);
}
Run11 Run12
LegacyJIT-x64 1 попугай 1.25 попугая
55/85 2.4 Практика: Interface method dispatch
Часть 2.5
Inlining
56/85 2.5 Практика: Inlining
Исходники .NET Framework
57/85 2.5 Практика: Inlining
Inlining — это сложно
// mscorlib/system/decimal.cs,158
// Constructs a Decimal from an integer value.
public Decimal(int value) {
// JIT today can't inline methods that contains "starg"
// opcode. For more details, see DevDiv Bugs 81184:
// x86 JIT CQ: Removing the inline striction of "starg".
int value_copy = value;
if (value_copy >= 0) {
flags = 0;
}
else {
flags = SignMask;
value_copy = -value_copy;
}
lo = value_copy;
mid = 0;
hi = 0;
}
58/85 2.5 Практика: Inlining
Проведём опыт
[Benchmark]
int Calc() => WithoutStarg(0x11) + WithStarg(0x12);
int WithoutStarg(int value) => value;
int WithStarg(int value)
{
if (value < 0)
value = -value;
return value;
}
59/85 2.5 Практика: Inlining
Проведём опыт
[Benchmark]
int Calc() => WithoutStarg(0x11) + WithStarg(0x12);
int WithoutStarg(int value) => value;
int WithStarg(int value)
{
if (value < 0)
value = -value;
return value;
}
LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64
1 попугай 0 попугаев 1 попугай
59/85 2.5 Практика: Inlining
Как же так?
LegacyJIT-x64
; LegacyJIT-x64
mov ecx,23h
ret
60/85 2.5 Практика: Inlining
Как же так?
LegacyJIT-x64
; LegacyJIT-x64
mov ecx,23h
ret
RyuJIT-x64
// Inline expansion aborted due to opcode
// [06] OP_starg.s in method
// Program:WithStarg(int):int:this
60/85 2.5 Практика: Inlining
Ещё одна загадка
61/85 2.5 Практика: Inlining
Отгадка
62/85 2.5 Практика: Inlining
Часть 2.6
Readonly fields
63/85 2.6 Практика: Readonly fields
Почитаем блог Джона Скита
64/85 2.6 Практика: Readonly fields
Проведём ещё один опыт
public struct Int256
{
private readonly long bits0, bits1, bits2, bits3;
public Int256(long bits0, long bits1, long bits2, long bits3)
{
this.bits0 = bits0; this.bits1 = bits1;
this.bits2 = bits2; this.bits3 = bits3;
}
public long Bits0 => bits0; public long Bits1 => bits1;
public long Bits2 => bits2; public long Bits3 => bits3;
}
private Int256 a = new Int256(1L, 5L, 10L, 100L);
private readonly Int256 b = new Int256(1L, 5L, 10L, 100L);
[Benchmark] public long GetValue() =>
a.Bits0 + a.Bits1 + a.Bits2 + a.Bits3;
[Benchmark] public long GetReadOnlyValue() =>
b.Bits0 + b.Bits1 + b.Bits2 + b.Bits3;
65/85 2.6 Практика: Readonly fields
Проведём ещё один опыт
public struct Int256
{
private readonly long bits0, bits1, bits2, bits3;
public Int256(long bits0, long bits1, long bits2, long bits3)
{
this.bits0 = bits0; this.bits1 = bits1;
this.bits2 = bits2; this.bits3 = bits3;
}
public long Bits0 => bits0; public long Bits1 => bits1;
public long Bits2 => bits2; public long Bits3 => bits3;
}
private Int256 a = new Int256(1L, 5L, 10L, 100L);
private readonly Int256 b = new Int256(1L, 5L, 10L, 100L);
[Benchmark] public long GetValue() =>
a.Bits0 + a.Bits1 + a.Bits2 + a.Bits3;
[Benchmark] public long GetReadOnlyValue() =>
b.Bits0 + b.Bits1 + b.Bits2 + b.Bits3;
LegacyJIT-x64 RyuJIT-x64
GetValue 1 попугай 1 попугай
GetReadOnlyValue 6.2 попугая 7.6 попугая
65/85 2.6 Практика: Readonly fields
Как же так?
; GetValue
IL_0000: ldarg.0
IL_0001: ldflda valuetype Program::a
IL_0006: call instance int64 Int256::get_Bits0()
; GetReadOnlyValue
IL_0000: ldarg.0
IL_0001: ldfld valuetype Program::b
IL_0006: stloc.0
IL_0007: ldloca.s 0
IL_0009: call instance int64 Int256::get_Bits0()
См. также: Jon Skeet, Micro-optimization: the surprising inefficiency of readonly fields
66/85 2.6 Практика: Readonly fields
Часть 2.7
SIMD
67/85 2.7 Практика: SIMD
Поговорим про SIMD
private struct MyVector
{
public float X, Y, Z, W;
public MyVector(float x, float y, float z, float w)
{
X = x; Y = y; Z = z; W = w;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MyVector operator *(MyVector left, MyVector right)
{
return new MyVector(left.X * right.X, left.Y * right.Y,
left.Z * right.Z, left.W * right.W);
}
}
private Vector4 vector1, vector2, vector3;
private MyVector myVector1, myVector2, myVector3;
[Benchmark] public void MyMul() => myVector3 = myVector1 * myVector2;
[Benchmark] public void BclMul() => vector3 = vector1 * vector2;
68/85 2.7 Практика: SIMD
Поговорим про SIMD
private struct MyVector
{
public float X, Y, Z, W;
public MyVector(float x, float y, float z, float w)
{
X = x; Y = y; Z = z; W = w;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MyVector operator *(MyVector left, MyVector right)
{
return new MyVector(left.X * right.X, left.Y * right.Y,
left.Z * right.Z, left.W * right.W);
}
}
private Vector4 vector1, vector2, vector3;
private MyVector myVector1, myVector2, myVector3;
[Benchmark] public void MyMul() => myVector3 = myVector1 * myVector2;
[Benchmark] public void BclMul() => vector3 = vector1 * vector2;
LegacyJIT-x64 RyuJIT-x64
MyMul 34 попугая 5 попугаев
BclMul 34 попугая 1 попугай
68/85 2.7 Практика: SIMD
Как же так?
; LegacyJIT-x64
; MyMul, BclMul
; ...
movss xmm3,dword ptr [rsp+40h]
mulss xmm3,dword ptr [rsp+30h]
movss xmm2,dword ptr [rsp+44h]
mulss xmm2,dword ptr [rsp+34h]
movss xmm1,dword ptr [rsp+48h]
mulss xmm1,dword ptr [rsp+38h]
movss xmm0,dword ptr [rsp+4Ch]
mulss xmm0,dword ptr [rsp+3Ch]
xor eax,eax
mov qword ptr [rsp],rax
mov qword ptr [rsp+8],rax
lea rax,[rsp]
movss dword ptr [rax],xmm3
movss dword ptr [rax+4],xmm2
movss dword ptr [rax+8],xmm1
movss dword ptr [rax+0Ch],xmm0
; ...
; RyuJIT-x64
; MyMul
; ...
vmulss xmm0,xmm0,xmm4
vmulss xmm1,xmm1,xmm5
vmulss xmm2,xmm2,xmm6
vmulss xmm3,xmm3,xmm7
; ...
; BclMul
vmovupd xmm0,xmmword ptr [rcx+8]
vmovupd xmm1,xmmword ptr [rcx+18h]
vmulps xmm0,xmm0,xmm1
vmovupd xmmword ptr [rcx+28h],xmm0
69/85 2.7 Практика: SIMD
Часть 2.8
Constant folding
70/85 2.8 Практика: Constant folding
Интересный issue на dotnet/coreclr
71/85 2.8 Практика: Constant folding
Учимся извлекать корни
double Sqrt13() =>
Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */
+ Math.Sqrt(13);
VS
double Sqrt14() =>
Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */
+ Math.Sqrt(13) + Math.Sqrt(14);
72/85 2.8 Практика: Constant folding
Учимся извлекать корни
double Sqrt13() =>
Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */
+ Math.Sqrt(13);
VS
double Sqrt14() =>
Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */
+ Math.Sqrt(13) + Math.Sqrt(14);
RyuJIT-x64
Sqrt13 ≈ 93ns
Sqrt14 0 ns
72/85 2.8 Практика: Constant folding
Как же так?
RyuJIT-x64, Sqrt13
vsqrtsd xmm0,xmm0,mmword ptr [7FF94F9E4D28h]
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D30h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D38h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D40h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D48h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D50h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D58h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D60h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D68h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D70h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D78h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D80h]
vaddsd xmm0,xmm0,xmm1
vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D88h]
vaddsd xmm0,xmm0,xmm1
ret
73/85 2.8 Практика: Constant folding
Как же так?
RyuJIT-x64, Sqrt14
vmovsd xmm0,qword ptr [7FF94F9C4C80h]
ret
74/85 2.8 Практика: Constant folding
Как же так?
Большое дерево выражения
* stmtExpr void (top level) (IL 0x000... ???)
| /--* mathFN double sqrt
| | --* dconst double 13.000000000000000
| /--* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 12.000000000000000
| | --* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 11.000000000000000
| | --* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 10.000000000000000
| | --* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 9.0000000000000000
| | --* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 8.0000000000000000
| | --* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 7.0000000000000000
| | --* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 6.0000000000000000
| | --* + double
| | | /--* mathFN double sqrt
| | | | --* dconst double 5.0000000000000000
// ...
75/85 2.8 Практика: Constant folding
Как же так?
Constant folding в действии
N001 [000001] dconst 1.0000000000000000 => $c0 {DblCns[1.000000]}
N002 [000002] mathFN => $c0 {DblCns[1.000000]}
N003 [000003] dconst 2.0000000000000000 => $c1 {DblCns[2.000000]}
N004 [000004] mathFN => $c2 {DblCns[1.414214]}
N005 [000005] + => $c3 {DblCns[2.414214]}
N006 [000006] dconst 3.0000000000000000 => $c4 {DblCns[3.000000]}
N007 [000007] mathFN => $c5 {DblCns[1.732051]}
N008 [000008] + => $c6 {DblCns[4.146264]}
N009 [000009] dconst 4.0000000000000000 => $c7 {DblCns[4.000000]}
N010 [000010] mathFN => $c1 {DblCns[2.000000]}
N011 [000011] + => $c8 {DblCns[6.146264]}
N012 [000012] dconst 5.0000000000000000 => $c9 {DblCns[5.000000]}
N013 [000013] mathFN => $ca {DblCns[2.236068]}
N014 [000014] + => $cb {DblCns[8.382332]}
N015 [000015] dconst 6.0000000000000000 => $cc {DblCns[6.000000]}
N016 [000016] mathFN => $cd {DblCns[2.449490]}
N017 [000017] + => $ce {DblCns[10.831822]}
N018 [000018] dconst 7.0000000000000000 => $cf {DblCns[7.000000]}
N019 [000019] mathFN => $d0 {DblCns[2.645751]}
N020 [000020] + => $d1 {DblCns[13.477573]}
...
76/85 2.8 Практика: Constant folding
Часть 2.9
Instruction level parallelism
77/85 2.9 Практика: Instruction level parallelism
Неожиданные perf-эффекты
1
Справедливо для RyuJIT RC, ныне пофикшено
78/85 2.9 Практика: Instruction level parallelism
Задачка
private double[] x = new double[11];
[Benchmark]
public double Calc()
{
double sum = 0.0;
for (int i = 1; i < x.Length; i++)
sum += 1.0 / (i * i) * x[i];
return sum;
}
79/85 2.9 Практика: Instruction level parallelism
Задачка
private double[] x = new double[11];
[Benchmark]
public double Calc()
{
double sum = 0.0;
for (int i = 1; i < x.Length; i++)
sum += 1.0 / (i * i) * x[i];
return sum;
}
LegacyJIT-x64 RyuJIT-x641
Calc 1 попугай 2 попугая
1
RyuJIT RC
79/85 2.9 Практика: Instruction level parallelism
Как же так?
; LegacyJIT-x64
; eax = i
mov eax,r8d
; eax = i*i
imul eax,r8d
; xmm0=i*i
cvtsi2sd xmm0,eax
; xmm1=1
movsd xmm1,
mmword ptr [7FF9141145E0h]
; xmm1=1/(i*i)
divsd xmm1,xmm0
; xmm1=1/(i*i)*x[i]
mulsd xmm1,
mmword ptr [rdx+r9+10h]
; xmm1 = sum + 1/(i*i)*x[i]
addsd xmm1,xmm2
; sum = sum + 1/(i*i)*x[i]
movapd xmm2,xmm1
; RyuJIT-x64
; r8d = i
mov r8d,eax
; r8d = i*i
imul r8d,eax
; xmm1=i*i
vcvtsi2sd xmm1,xmm1,r8d
; xmm2=1
vmovsd xmm2,
qword ptr [7FF9140E4398h]
; xmm2=1/(i*i)
vdivsd xmm2,xmm2,xmm1
mov r8,rdx
movsxd r9,eax
; xmm1 = 1/(i*i)
vmovaps xmm1,xmm2
; xmm1 = 1/(i*i)*x[i]
vmulsd xmm1,xmm1,
mmword ptr [r8+r9*8+10h]
; sum += 1/(i*i)*x[i]
vaddsd xmm0,xmm0,xmm1
80/85 2.9 Практика: Instruction level parallelism
Часть 3
Заключение
81/85 3. Заключение
Отказ от ответственности
82/85 3. Заключение
Отказ от ответственности
• Все представленные выводы и бенчмарки
могут быть враньём
82/85 3. Заключение
Отказ от ответственности
• Все представленные выводы и бенчмарки
могут быть враньём
• Использование BenchmarkDotNet не делает
ваш бенчмарк правильным
82/85 3. Заключение
Отказ от ответственности
• Все представленные выводы и бенчмарки
могут быть враньём
• Использование BenchmarkDotNet не делает
ваш бенчмарк правильным
• Использование самописных бенчмарков не
делает выводы ложными
82/85 3. Заключение
Сегодня мы узнали
83/85 3. Заключение
Сегодня мы узнали
• Бенчмаркинг и прочие замеры
производительности — это сложно
83/85 3. Заключение
Сегодня мы узнали
• Бенчмаркинг и прочие замеры
производительности — это сложно
• Бенчмаркинг требует очень много сил,
знаний, времени и нервов
83/85 3. Заключение
Сегодня мы узнали
• Бенчмаркинг и прочие замеры
производительности — это сложно
• Бенчмаркинг требует очень много сил,
знаний, времени и нервов
• Бенчмарк без анализа — плохой бенчмарк
83/85 3. Заключение
Методическая литература
Для успешных микробенчмарков нужно очень много знать:
84/85 3. Заключение
Вопросы?
Андрей Акиньшин
http://aakinshin.net
https://github.com/AndreyAkinshin
https://twitter.com/andrey_akinshin
andrey.akinshin@gmail.com
85/85 3. Заключение

More Related Content

Similar to Теория и практика .NET-бенчмаркинга (02.11.2016, Екатеринбург)

Андрей Зайцев - TDD в кровавом энтерпрайзе
Андрей Зайцев - TDD в кровавом энтерпрайзеАндрей Зайцев - TDD в кровавом энтерпрайзе
Андрей Зайцев - TDD в кровавом энтерпрайзеElias Fofanov
 
КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения
КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения
КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения Iosif Itkin
 
Сложности performance-тестирования
Сложности performance-тестированияСложности performance-тестирования
Сложности performance-тестированияAndrey Akinshin
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.Igor Shkulipa
 
Краткое руководство ДСК
Краткое руководство ДСККраткое руководство ДСК
Краткое руководство ДСКMarat Akhmetov
 
Инновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVIS
Инновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVISИнновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVIS
Инновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVIS10X Engineering
 
Борзунов Александр, Cpmoptimize
Борзунов Александр, CpmoptimizeБорзунов Александр, Cpmoptimize
Борзунов Александр, CpmoptimizeDarya Zubova
 
Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...
Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...
Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...Alexander Borzunov
 
Измерение времени выполнения коллективных операций обменов информацией между ...
Измерение времени выполнения коллективных операций обменов информацией между ...Измерение времени выполнения коллективных операций обменов информацией между ...
Измерение времени выполнения коллективных операций обменов информацией между ...Mikhail Kurnosov
 
CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!
CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!
CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!CodeFest
 
Artem Bezruchko "Test Strategy in SCRUM"
Artem Bezruchko "Test Strategy in SCRUM"Artem Bezruchko "Test Strategy in SCRUM"
Artem Bezruchko "Test Strategy in SCRUM"Fwdays
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1Michael Karpov
 
Слои тестового фрамеворка. Что? Где? Когда?
Слои тестового фрамеворка. Что? Где? Когда?Слои тестового фрамеворка. Что? Где? Когда?
Слои тестового фрамеворка. Что? Где? Когда?COMAQA.BY
 
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...SQALab
 
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...Maksim Grinevich
 
Практические аспекты нагрузочного тестирования
Практические аспекты нагрузочного тестированияПрактические аспекты нагрузочного тестирования
Практические аспекты нагрузочного тестированияAlexey Kachalin
 
АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019
АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019
АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019GoQA
 
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...yaevents
 
Многопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметкиМногопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметкиDotNetConf
 
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполненияАвтотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполненияSQALab
 

Similar to Теория и практика .NET-бенчмаркинга (02.11.2016, Екатеринбург) (20)

Андрей Зайцев - TDD в кровавом энтерпрайзе
Андрей Зайцев - TDD в кровавом энтерпрайзеАндрей Зайцев - TDD в кровавом энтерпрайзе
Андрей Зайцев - TDD в кровавом энтерпрайзе
 
КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения
КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения
КГТУ Лекция 6: Обеспечение Качества Программного Обеспечения
 
Сложности performance-тестирования
Сложности performance-тестированияСложности performance-тестирования
Сложности performance-тестирования
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.
 
Краткое руководство ДСК
Краткое руководство ДСККраткое руководство ДСК
Краткое руководство ДСК
 
Инновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVIS
Инновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVISИнновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVIS
Инновационные научно-учебные лабораторные стенды на базе NI myDAQ, NI ELVIS
 
Борзунов Александр, Cpmoptimize
Борзунов Александр, CpmoptimizeБорзунов Александр, Cpmoptimize
Борзунов Александр, Cpmoptimize
 
Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...
Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...
Автоматическая оптимизация алгоритмов с помощью быстрого возведения матриц в ...
 
Измерение времени выполнения коллективных операций обменов информацией между ...
Измерение времени выполнения коллективных операций обменов информацией между ...Измерение времени выполнения коллективных операций обменов информацией между ...
Измерение времени выполнения коллективных операций обменов информацией между ...
 
CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!
CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!
CodeFest 2014. Шипилёв А. — Java Benchmarking: как два таймстампа записать!
 
Artem Bezruchko "Test Strategy in SCRUM"
Artem Bezruchko "Test Strategy in SCRUM"Artem Bezruchko "Test Strategy in SCRUM"
Artem Bezruchko "Test Strategy in SCRUM"
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1
 
Слои тестового фрамеворка. Что? Где? Когда?
Слои тестового фрамеворка. Что? Где? Когда?Слои тестового фрамеворка. Что? Где? Когда?
Слои тестового фрамеворка. Что? Где? Когда?
 
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
 
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
Промышленный подход к автоматизации тестирования или Keyword-driven testing в...
 
Практические аспекты нагрузочного тестирования
Практические аспекты нагрузочного тестированияПрактические аспекты нагрузочного тестирования
Практические аспекты нагрузочного тестирования
 
АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019
АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019
АНТОН СЕРПУТЬКО «Start performance testing from scratch» QADay 2019
 
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
 
Многопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметкиМногопоточное программирование на C#, путевые заметки
Многопоточное программирование на C#, путевые заметки
 
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполненияАвтотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
 

More from Andrey Akinshin

Поговорим про performance-тестирование
Поговорим про performance-тестированиеПоговорим про performance-тестирование
Поговорим про performance-тестированиеAndrey Akinshin
 
Сложности микробенчмаркинга
Сложности микробенчмаркингаСложности микробенчмаркинга
Сложности микробенчмаркингаAndrey Akinshin
 
Поговорим про память
Поговорим про памятьПоговорим про память
Поговорим про памятьAndrey Akinshin
 
Кроссплатформенный .NET и как там дела с Mono и CoreCLR
Кроссплатформенный .NET и как там дела с Mono и CoreCLRКроссплатформенный .NET и как там дела с Mono и CoreCLR
Кроссплатформенный .NET и как там дела с Mono и CoreCLRAndrey Akinshin
 
Let’s talk about microbenchmarking
Let’s talk about microbenchmarkingLet’s talk about microbenchmarking
Let’s talk about microbenchmarkingAndrey Akinshin
 
Поговорим про арифметику
Поговорим про арифметикуПоговорим про арифметику
Поговорим про арифметикуAndrey Akinshin
 
Подружили CLR и JVM в Project Rider
Подружили CLR и JVM в Project RiderПодружили CLR и JVM в Project Rider
Подружили CLR и JVM в Project RiderAndrey Akinshin
 
Что нам готовит грядущий C#7?
Что нам готовит грядущий C#7?Что нам готовит грядущий C#7?
Что нам готовит грядущий C#7?Andrey Akinshin
 
.NET 2015: Будущее рядом
.NET 2015: Будущее рядом.NET 2015: Будущее рядом
.NET 2015: Будущее рядомAndrey Akinshin
 
Продолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийПродолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийAndrey Akinshin
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийAndrey Akinshin
 
Поговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийПоговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийAndrey Akinshin
 
Практические приёмы оптимизации .NET-приложений
Практические приёмы оптимизации .NET-приложенийПрактические приёмы оптимизации .NET-приложений
Практические приёмы оптимизации .NET-приложенийAndrey Akinshin
 
Поговорим о различных версиях .NET
Поговорим о различных версиях .NETПоговорим о различных версиях .NET
Поговорим о различных версиях .NETAndrey Akinshin
 
Низкоуровневые оптимизации .NET-приложений
Низкоуровневые оптимизации .NET-приложенийНизкоуровневые оптимизации .NET-приложений
Низкоуровневые оптимизации .NET-приложенийAndrey Akinshin
 
Основы работы с Git
Основы работы с GitОсновы работы с Git
Основы работы с GitAndrey Akinshin
 
Сборка мусора в .NET
Сборка мусора в .NETСборка мусора в .NET
Сборка мусора в .NETAndrey Akinshin
 

More from Andrey Akinshin (18)

Поговорим про performance-тестирование
Поговорим про performance-тестированиеПоговорим про performance-тестирование
Поговорим про performance-тестирование
 
Сложности микробенчмаркинга
Сложности микробенчмаркингаСложности микробенчмаркинга
Сложности микробенчмаркинга
 
Поговорим про память
Поговорим про памятьПоговорим про память
Поговорим про память
 
Кроссплатформенный .NET и как там дела с Mono и CoreCLR
Кроссплатформенный .NET и как там дела с Mono и CoreCLRКроссплатформенный .NET и как там дела с Mono и CoreCLR
Кроссплатформенный .NET и как там дела с Mono и CoreCLR
 
Let’s talk about microbenchmarking
Let’s talk about microbenchmarkingLet’s talk about microbenchmarking
Let’s talk about microbenchmarking
 
Поговорим про арифметику
Поговорим про арифметикуПоговорим про арифметику
Поговорим про арифметику
 
Подружили CLR и JVM в Project Rider
Подружили CLR и JVM в Project RiderПодружили CLR и JVM в Project Rider
Подружили CLR и JVM в Project Rider
 
Что нам готовит грядущий C#7?
Что нам готовит грядущий C#7?Что нам готовит грядущий C#7?
Что нам готовит грядущий C#7?
 
.NET 2015: Будущее рядом
.NET 2015: Будущее рядом.NET 2015: Будущее рядом
.NET 2015: Будущее рядом
 
Продолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийПродолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложений
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложений
 
Поговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийПоговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложений
 
Практические приёмы оптимизации .NET-приложений
Практические приёмы оптимизации .NET-приложенийПрактические приёмы оптимизации .NET-приложений
Практические приёмы оптимизации .NET-приложений
 
Поговорим о различных версиях .NET
Поговорим о различных версиях .NETПоговорим о различных версиях .NET
Поговорим о различных версиях .NET
 
Низкоуровневые оптимизации .NET-приложений
Низкоуровневые оптимизации .NET-приложенийНизкоуровневые оптимизации .NET-приложений
Низкоуровневые оптимизации .NET-приложений
 
Основы работы с Git
Основы работы с GitОсновы работы с Git
Основы работы с Git
 
Сборка мусора в .NET
Сборка мусора в .NETСборка мусора в .NET
Сборка мусора в .NET
 
Phd presentation
Phd presentationPhd presentation
Phd presentation
 

Теория и практика .NET-бенчмаркинга (02.11.2016, Екатеринбург)

  • 1. Теория и практика .NET-бенчмаркинга Андрей Акиньшин, JetBrains Екатеринбург, 02.11.2016 1/85
  • 3. Часть 1.1 Почему мы об этом говорим? 3/85 1.1 Теория: Почему мы об этом говорим?
  • 4. StackOverflow Люди любят бенчмаркать 4/85 1.1 Теория: Почему мы об этом говорим?
  • 5. StackOverflow Типичный вопрос 5/85 1.1 Теория: Почему мы об этом говорим?
  • 6. Habrahabr Некоторые делают выводы и пишут статьи 6/85 1.1 Теория: Почему мы об этом говорим?
  • 7. Применения бенчмарков 7/85 1.1 Теория: Почему мы об этом говорим?
  • 8. Применения бенчмарков • Performance analysis 7/85 1.1 Теория: Почему мы об этом говорим?
  • 9. Применения бенчмарков • Performance analysis • Сравнение алгоритмов 7/85 1.1 Теория: Почему мы об этом говорим?
  • 10. Применения бенчмарков • Performance analysis • Сравнение алгоритмов • Оценка улучшений производительности 7/85 1.1 Теория: Почему мы об этом говорим?
  • 11. Применения бенчмарков • Performance analysis • Сравнение алгоритмов • Оценка улучшений производительности • Анализ регрессии 7/85 1.1 Теория: Почему мы об этом говорим?
  • 12. Применения бенчмарков • Performance analysis • Сравнение алгоритмов • Оценка улучшений производительности • Анализ регрессии • . . . 7/85 1.1 Теория: Почему мы об этом говорим?
  • 13. Применения бенчмарков • Performance analysis • Сравнение алгоритмов • Оценка улучшений производительности • Анализ регрессии • . . . • Научный интерес 7/85 1.1 Теория: Почему мы об этом говорим?
  • 14. Применения бенчмарков • Performance analysis • Сравнение алгоритмов • Оценка улучшений производительности • Анализ регрессии • . . . • Научный интерес • Маркетинг 7/85 1.1 Теория: Почему мы об этом говорим?
  • 15. Применения бенчмарков • Performance analysis • Сравнение алгоритмов • Оценка улучшений производительности • Анализ регрессии • . . . • Научный интерес • Маркетинг • Весёлое времяпрепровождение 7/85 1.1 Теория: Почему мы об этом говорим?
  • 16. Часть 1.2 Общая методология 8/85 1.2 Теория: Общая методология
  • 17. План performance-работ 9/85 1.2 Теория: Общая методология
  • 18. План performance-работ 1 Поставить задачу 9/85 1.2 Теория: Общая методология
  • 19. План performance-работ 1 Поставить задачу 2 Выбрать метрики 9/85 1.2 Теория: Общая методология
  • 20. План performance-работ 1 Поставить задачу 2 Выбрать метрики 3 Выбрать инструмент 9/85 1.2 Теория: Общая методология
  • 21. План performance-работ 1 Поставить задачу 2 Выбрать метрики 3 Выбрать инструмент 4 Провести эксперимент 9/85 1.2 Теория: Общая методология
  • 22. План performance-работ 1 Поставить задачу 2 Выбрать метрики 3 Выбрать инструмент 4 Провести эксперимент 5 Получить результаты 9/85 1.2 Теория: Общая методология
  • 23. План performance-работ 1 Поставить задачу 2 Выбрать метрики 3 Выбрать инструмент 4 Провести эксперимент 5 Получить результаты 6 Выполнить анализ и сделать выводы 9/85 1.2 Теория: Общая методология
  • 24. План performance-работ 1 Поставить задачу 2 Выбрать метрики 3 Выбрать инструмент 4 Провести эксперимент 5 Получить результаты 6 Выполнить анализ и сделать выводы Анализ полученных данных — самый важный этап 9/85 1.2 Теория: Общая методология
  • 25. Виды performance-работ 10/85 1.2 Теория: Общая методология
  • 26. Виды performance-работ • Profiling 10/85 1.2 Теория: Общая методология
  • 27. Виды performance-работ • Profiling • Monitoring 10/85 1.2 Теория: Общая методология
  • 28. Виды performance-работ • Profiling • Monitoring • Performance tests 10/85 1.2 Теория: Общая методология
  • 29. Виды performance-работ • Profiling • Monitoring • Performance tests • Benchmarking (micro/macro) 10/85 1.2 Теория: Общая методология
  • 30. Виды performance-работ • Profiling • Monitoring • Performance tests • Benchmarking (micro/macro) • . . . 10/85 1.2 Теория: Общая методология
  • 31. Performance spaces 11/85 1.2 Теория: Общая методология
  • 32. Performance spaces Исходный код 11/85 1.2 Теория: Общая методология
  • 33. Performance spaces Исходный код X Окружение 11/85 1.2 Теория: Общая методология
  • 34. Performance spaces Исходный код X Окружение X Входные данные 11/85 1.2 Теория: Общая методология
  • 35. Performance spaces Исходный код X Окружение X Входные данные ⇓ Распределение 11/85 1.2 Теория: Общая методология
  • 36. Performance spaces Непонимание проблематики влечёт за собой следующие проблемы: 12/85 1.2 Теория: Общая методология
  • 37. Performance spaces Непонимание проблематики влечёт за собой следующие проблемы: • Легко обмануть себя, сделать неправильные выводы, принять вредные бизнес-решения 12/85 1.2 Теория: Общая методология
  • 38. Performance spaces Непонимание проблематики влечёт за собой следующие проблемы: • Легко обмануть себя, сделать неправильные выводы, принять вредные бизнес-решения • Легко пропустить важную конфигурацию, которая испортит жизнь в продакшене 12/85 1.2 Теория: Общая методология
  • 39. Performance spaces Непонимание проблематики влечёт за собой следующие проблемы: • Легко обмануть себя, сделать неправильные выводы, принять вредные бизнес-решения • Легко пропустить важную конфигурацию, которая испортит жизнь в продакшене • Легко повестись на кривые бенчмарки или чёрный маркетинг 12/85 1.2 Теория: Общая методология
  • 40. Окружение C# compiler старый csc / Roslyn CLR CLR2 / CLR4 / CoreCLR / Mono OS Windows / Linux / MacOS / FreeBSD JIT LegacyJIT-x86 / LegacyJIT-x64 / RyuJIT-x64 GC MS (разные CLR) / Mono (Boehm/Sgen) Toolchain JIT / NGen / .NET Native Hardware тысячи его . . . . . . 13/85 1.2 Теория: Общая методология
  • 41. Окружение C# compiler старый csc / Roslyn CLR CLR2 / CLR4 / CoreCLR / Mono OS Windows / Linux / MacOS / FreeBSD JIT LegacyJIT-x86 / LegacyJIT-x64 / RyuJIT-x64 GC MS (разные CLR) / Mono (Boehm/Sgen) Toolchain JIT / NGen / .NET Native Hardware тысячи его . . . . . . И не забываем про версии, много-много версий 13/85 1.2 Теория: Общая методология
  • 42. Окружение и запуск Советы по запуску бенчмарков 14/85 1.2 Теория: Общая методология
  • 43. Окружение и запуск Советы по запуску бенчмарков • Release build 14/85 1.2 Теория: Общая методология
  • 44. Окружение и запуск Советы по запуску бенчмарков • Release build • Без дебаггера 14/85 1.2 Теория: Общая методология
  • 45. Окружение и запуск Советы по запуску бенчмарков • Release build • Без дебаггера • Выключите другие приложения 14/85 1.2 Теория: Общая методология
  • 46. Окружение и запуск Советы по запуску бенчмарков • Release build • Без дебаггера • Выключите другие приложения • Используйте максимальную производительность 14/85 1.2 Теория: Общая методология
  • 47. Требования к бенчмарку 15/85 1.2 Теория: Общая методология
  • 48. Требования к бенчмарку • Надёжность 15/85 1.2 Теория: Общая методология
  • 49. Требования к бенчмарку • Надёжность • Точность 15/85 1.2 Теория: Общая методология
  • 50. Требования к бенчмарку • Надёжность • Точность • Воспроизводимость 15/85 1.2 Теория: Общая методология
  • 51. Требования к бенчмарку • Надёжность • Точность • Воспроизводимость • Изолированность 15/85 1.2 Теория: Общая методология
  • 52. Требования к бенчмарку • Надёжность • Точность • Воспроизводимость • Изолированность • Переносимость 15/85 1.2 Теория: Общая методология
  • 53. Требования к бенчмарку • Надёжность • Точность • Воспроизводимость • Изолированность • Переносимость • Простота 15/85 1.2 Теория: Общая методология
  • 54. Требования к бенчмарку • Надёжность • Точность • Воспроизводимость • Изолированность • Переносимость • Простота • Честность 15/85 1.2 Теория: Общая методология
  • 55. BenchmarkDotNet 16/85 1.2 Теория: Общая методология
  • 56. Часть 1.3 Таймеры 17/85 1.3 Теория: Таймеры
  • 57. DateTime vs Stopwatch var start = DateTime.Now; Foo(); var finish = DateTime.Now; var time = (finish - start).TotalMilliseconds; vs var sw = Stopwatch.StartNew(); Foo(); sw.Stop(); var time = sw.ElapsedMilliseconds; 18/85 1.3 Теория: Таймеры
  • 59. Характеристики таймеров • Монотонность замеры должны неуменьшаться 19/85 1.3 Теория: Таймеры
  • 60. Характеристики таймеров • Монотонность замеры должны неуменьшаться • Resolution минимальное положительное время между замерами 19/85 1.3 Теория: Таймеры
  • 61. Характеристики таймеров • Монотонность замеры должны неуменьшаться • Resolution минимальное положительное время между замерами • Latency время на получение замера 19/85 1.3 Теория: Таймеры
  • 63. DateTime.UtcNow OS Implementation Windows GetSystemTimeAsFileTime Linux gettimeofday 20/85 1.3 Теория: Таймеры
  • 64. DateTime.UtcNow OS Implementation Windows GetSystemTimeAsFileTime Linux gettimeofday OS Runtime Time∗ Latency Windows Full/Core ≈7–8ns Latency Windows Mono ≈30–31ns Resolution Windows Any ≈0.5..15.625ms 20/85 1.3 Теория: Таймеры
  • 65. DateTime.UtcNow OS Implementation Windows GetSystemTimeAsFileTime Linux gettimeofday OS Runtime Time∗ Latency Windows Full/Core ≈7–8ns Latency Windows Mono ≈30–31ns Resolution Windows Any ≈0.5..15.625ms Latency Linux Mono ≈26–30ns Resolution Linux Mono ≈1µs 20/85 1.3 Теория: Таймеры
  • 66. DateTime.UtcNow OS Implementation Windows GetSystemTimeAsFileTime Linux gettimeofday OS Runtime Time∗ Latency Windows Full/Core ≈7–8ns Latency Windows Mono ≈30–31ns Resolution Windows Any ≈0.5..15.625ms Latency Linux Mono ≈26–30ns Resolution Linux Mono ≈1µs ∗ Intel i7-4702MQ CPU 2.20GHz См. также: http://aakinshin.net/en/blog/dotnet/datetime/ 20/85 1.3 Теория: Таймеры
  • 68. Stopwatch.GetTimestamp() Hardware timers • NA • TSC (Variant / Constant / Invariant) • ACPI PM (Freq = 3.579545 MHz) • HPET (Freq = 14.31818 MHz) 21/85 1.3 Теория: Таймеры
  • 69. Stopwatch.GetTimestamp() Hardware timers • NA • TSC (Variant / Constant / Invariant) • ACPI PM (Freq = 3.579545 MHz) • HPET (Freq = 14.31818 MHz) Implementation • Windows: QueryPerformanceCounter • Linux: clock_gettime / mach_absolute_time / gettimeofday 21/85 1.3 Теория: Таймеры
  • 70. Stopwatch.GetTimestamp() Runtime OS Timer 1 tick Latency Resolution Full Win TSC 300-400ns 15-18ns 300-400ns Full Win HPET 69.8ns 500-800ns ≈Latency Full Win NA 100ns 7-10ns 0.5-55ms Mono Win TSC 100ns 35-45ns 300-400ns Mono Win HPET 100ns 500-800ns ≈Latency Mono Win NA 100ns 30-40ns 0.5-55ms Core Linux TSC 1ns 30-35ns ≈Latency Core Linux HPET/ACPI 1ns 500-800ns ≈Latency Mono Linux TSC 100ns 20-25ns 100ns Mono Linux HPET/ACPI 100ns 500-800ns ≈Latency Intel i7-4702MQ CPU 2.20GHz См. также: http://aakinshin.net/en/blog/dotnet/stopwatch/ 22/85 1.3 Теория: Таймеры
  • 71. Важно помнить про таймеры 23/85 1.3 Теория: Таймеры
  • 72. Важно помнить про таймеры • Важно понимать значения Latency и Resolution 23/85 1.3 Теория: Таймеры
  • 73. Важно помнить про таймеры • Важно понимать значения Latency и Resolution • 1 tick ≠ Resolution 23/85 1.3 Теория: Таймеры
  • 74. Важно помнить про таймеры • Важно понимать значения Latency и Resolution • 1 tick ≠ Resolution • Время может идти назад 23/85 1.3 Теория: Таймеры
  • 75. Важно помнить про таймеры • Важно понимать значения Latency и Resolution • 1 tick ≠ Resolution • Время может идти назад • Два последовательных замера могут быть равны 23/85 1.3 Теория: Таймеры
  • 76. Важно помнить про таймеры • Важно понимать значения Latency и Resolution • 1 tick ≠ Resolution • Время может идти назад • Два последовательных замера могут быть равны • Два последовательных замера могут различаться на миллисекунды 23/85 1.3 Теория: Таймеры
  • 77. Часть 1.4 Количество итераций 24/85 1.4 Теория: Количество итераций
  • 78. Микробенчмаркинг Плохой бенчмарк // Resolution(Stopwatch) = 466 ns // Latency(Stopwatch) = 18 ns var sw = Stopwatch.StartNew(); Foo(); // 100 ns sw.Stop(); WriteLine(sw.ElapsedMilliseconds); 25/85 1.4 Теория: Количество итераций
  • 79. Микробенчмаркинг Плохой бенчмарк // Resolution(Stopwatch) = 466 ns // Latency(Stopwatch) = 18 ns var sw = Stopwatch.StartNew(); Foo(); // 100 ns sw.Stop(); WriteLine(sw.ElapsedMilliseconds); Небольшое улучшение var sw = Stopwatch.StartNew(); for (int i = 0; i < N; i++) // (N * 100 + eps) ns Foo(); sw.Stop(); var total = sw.ElapsedTicks / Stopwatch.Frequency; WriteLine(total / N); 25/85 1.4 Теория: Количество итераций
  • 80. Прогрев Запустим бенчмарк несколько раз: int[] x = new int[128 * 1024 * 1024]; for (int iter = 0; iter < 5; iter++) { var sw = Stopwatch.StartNew(); for (int i = 0; i < x.Length; i += 16) x[i]++; sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); } 26/85 1.4 Теория: Количество итераций
  • 81. Прогрев Запустим бенчмарк несколько раз: int[] x = new int[128 * 1024 * 1024]; for (int iter = 0; iter < 5; iter++) { var sw = Stopwatch.StartNew(); for (int i = 0; i < x.Length; i += 16) x[i]++; sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); } Результат: 176 81 62 62 62 26/85 1.4 Теория: Количество итераций
  • 82. Несколько запусков метода Run 01 : 529.8674 ns/op Run 02 : 532.7541 ns/op Run 03 : 558.7448 ns/op Run 04 : 555.6647 ns/op Run 05 : 539.6401 ns/op Run 06 : 539.3494 ns/op Run 07 : 564.3222 ns/op Run 08 : 551.9544 ns/op Run 09 : 550.1608 ns/op Run 10 : 533.0634 ns/op 27/85 1.4 Теория: Количество итераций
  • 83. Несколько запусков бенчмарка 28/85 1.4 Теория: Количество итераций
  • 84. Простой случай Центральная предельная теорема спешит на помощь! 29/85 1.4 Теория: Количество итераций
  • 85. Но есть и сложные случаи 30/85 1.4 Теория: Количество итераций
  • 86. Часть 1.5 Различные сложности 31/85 1.5 Теория: Различные сложности
  • 87. Накладные расходы var sw = Stopwatch.StartNew(); int x = 0; for (int i = 0; i < N; i++) // overhead x++; // target operation sw.Stop(); 32/85 1.5 Теория: Различные сложности
  • 88. Изоляция бенчмарков Плохой бенчмарк var sw1 = Stopwatch.StartNew(); Foo(); sw1.Stop(); var sw2 = Stopwatch.StartNew(); Bar(); sw2.Stop(); 33/85 1.5 Теория: Различные сложности
  • 89. Изоляция бенчмарков Плохой бенчмарк var sw1 = Stopwatch.StartNew(); Foo(); sw1.Stop(); var sw2 = Stopwatch.StartNew(); Bar(); sw2.Stop(); Вспомним про: • Interface method dispatch • Garbage collector and autotuning • Jitting 33/85 1.5 Теория: Различные сложности
  • 90. Борьба с оптимизациями 34/85 1.5 Теория: Различные сложности
  • 91. Борьба с оптимизациями • Dead code elimination 34/85 1.5 Теория: Различные сложности
  • 92. Борьба с оптимизациями • Dead code elimination • Inlining 34/85 1.5 Теория: Различные сложности
  • 93. Борьба с оптимизациями • Dead code elimination • Inlining • Constant folding 34/85 1.5 Теория: Различные сложности
  • 94. Борьба с оптимизациями • Dead code elimination • Inlining • Constant folding • Instruction Level Parallelism 34/85 1.5 Теория: Различные сложности
  • 95. Борьба с оптимизациями • Dead code elimination • Inlining • Constant folding • Instruction Level Parallelism • Branch prediction 34/85 1.5 Теория: Различные сложности
  • 96. Борьба с оптимизациями • Dead code elimination • Inlining • Constant folding • Instruction Level Parallelism • Branch prediction • . . . 34/85 1.5 Теория: Различные сложности
  • 97. Доступ к памяти Event Latency Scaled 1 CPU cycle 0.3 ns 1 s Level 1 cache access 0.9 ns 3 s Level 2 cache access 2.8 ns 9 s Level 3 cache access 12.9 ns 43 s Main memory access 120 ns 6 min Solid-state disk I/O 50-150 µs 2-6 days Rotational disk I/O 1-10 ms 1-12 months Internet: SF to NYC 40 ms 4 years Internet: SF to UK 81 ms 8 years Internet: SF to Australia 183 ms 19 years OS virtualization reboot 4 s 423 years SCSI command time-out 30 s 3000 years Hardware virtualization reboot 40 s 4000 years Physical system reboot 5 m 32 millenia © Systems Performance: Enterprise and the Cloud 35/85 1.5 Теория: Различные сложности
  • 98. Processor affinity 36/85 1.5 Теория: Различные сложности
  • 99. True sharing 37/85 1.5 Теория: Различные сложности
  • 100. False sharing 38/85 1.5 Теория: Различные сложности
  • 101. False sharing в действии private static int[] x = new int[1024]; private void Inc(int p) { for (int i = 0; i < 10000001; i++) x[p]++; } private void Run(int step) { var sw = Stopwatch.StartNew(); Task.WaitAll( Task.Factory.StartNew(() => Inc(0 * step)), Task.Factory.StartNew(() => Inc(1 * step)), Task.Factory.StartNew(() => Inc(2 * step)), Task.Factory.StartNew(() => Inc(3 * step))); Console.WriteLine(sw.ElapsedMilliseconds); } 39/85 1.5 Теория: Различные сложности
  • 102. False sharing в действии private static int[] x = new int[1024]; private void Inc(int p) { for (int i = 0; i < 10000001; i++) x[p]++; } private void Run(int step) { var sw = Stopwatch.StartNew(); Task.WaitAll( Task.Factory.StartNew(() => Inc(0 * step)), Task.Factory.StartNew(() => Inc(1 * step)), Task.Factory.StartNew(() => Inc(2 * step)), Task.Factory.StartNew(() => Inc(3 * step))); Console.WriteLine(sw.ElapsedMilliseconds); } Run(1) Run(256) ≈400ms ≈150ms 39/85 1.5 Теория: Различные сложности
  • 103. Бенчмаркинг — это сложно Anon et al., “A Measure of Transaction Processing Power” There are lies, damn lies and then there are performance measures. 40/85 1.5 Теория: Различные сложности
  • 105. Часть 2.1 Сложности нанобенчмаркинга 42/85 2.1 Практика: Сложности нанобенчмаркинга
  • 106. Задача Какой из методов работает быстрее? [MethodImpl(MethodImplOptions.NoInlining)] public void Empty0() {} [MethodImpl(MethodImplOptions.NoInlining)] public void Empty1() {} [MethodImpl(MethodImplOptions.NoInlining)] public void Empty2() {} [MethodImpl(MethodImplOptions.NoInlining)] public void Empty3() {} 43/85 2.1 Практика: Сложности нанобенчмаркинга
  • 107. Попытка решения Давайте забенчмаркаем! private void MeasureX() // X = 0, 1, 2, 3 { for (int i = 0; i < Rep; i++) { var sw = Stopwatch.StartNew(); for (int j = 0; j < N; j++) EmptyX(); // X = 0, 1, 2, 3 sw.Stop(); Write(sw.ElapsedMilliseconds + " "); } } 44/85 2.1 Практика: Сложности нанобенчмаркинга
  • 108. Попытка решения Давайте забенчмаркаем! private void MeasureX() // X = 0, 1, 2, 3 { for (int i = 0; i < Rep; i++) { var sw = Stopwatch.StartNew(); for (int j = 0; j < N; j++) EmptyX(); // X = 0, 1, 2, 3 sw.Stop(); Write(sw.ElapsedMilliseconds + " "); } } Empty0: 242 253 245 253 242 244 245 255 245 245 // Slow Empty1: 224 228 229 224 223 224 227 222 228 222 // Fast Empty2: 229 222 226 222 224 226 227 229 225 230 // Fast Empty3: 241 240 237 244 242 241 238 245 239 239 // Slow Intel Core i7 Haswell, RyuJIT-x64 44/85 2.1 Практика: Сложности нанобенчмаркинга
  • 109. Обратимся к классике Рекомендуемая литература: Agner Fog, “The microarchitecture of Intel, AMD and VIA CPUs. An optimization guide for assembly programmers and compiler makers.” 45/85 2.1 Практика: Сложности нанобенчмаркинга
  • 110. Обратимся к классике Рекомендуемая литература: Agner Fog, “The microarchitecture of Intel, AMD and VIA CPUs. An optimization guide for assembly programmers and compiler makers.” 3.8 Branch prediction in Intel Haswell, Broadwell and Skylake Pattern recognition for indirect jumps and calls. Indirect jumps and indirect calls are predicted well. 45/85 2.1 Практика: Сложности нанобенчмаркинга
  • 111. Воспользуемся верным инструментом 46/85 2.1 Практика: Сложности нанобенчмаркинга
  • 112. Старая добрая раскрутка цикла var sw = Stopwatch.StartNew(); for (int j = 0; j < N; j++) { Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); Empty0(); } sw.Stop(); 47/85 2.1 Практика: Сложности нанобенчмаркинга
  • 113. Раскрутим наш цикл Есть ли статистически значимая разница между замерами? 48/85 2.1 Практика: Сложности нанобенчмаркинга
  • 114. Работа над ошибками • Выключайте приложения во время бенчмаркинга! Результаты без запущенной VisualStudio: • Не пытайтесь оценивать статистическую значимость на глаз. 49/85 2.1 Практика: Сложности нанобенчмаркинга
  • 115. Часть 2.2 Работаем с памятью 50/85 2.2 Практика: Работаем с памятью
  • 116. Сумма элементов массива const int N = 1024; int[,] a = new int[N, N]; [Benchmark] public double SumIj() { var sum = 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) sum += a[i, j]; return sum; } [Benchmark] public double SumJi() { var sum = 0; for (int j = 0; j < N; j++) for (int i = 0; i < N; i++) sum += a[i, j]; return sum; } 51/85 2.2 Практика: Работаем с памятью
  • 117. Сумма элементов массива const int N = 1024; int[,] a = new int[N, N]; [Benchmark] public double SumIj() { var sum = 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) sum += a[i, j]; return sum; } [Benchmark] public double SumJi() { var sum = 0; for (int j = 0; j < N; j++) for (int i = 0; i < N; i++) sum += a[i, j]; return sum; } SumIj SumJi LegacyJIT-x86 1 попугай 3.5 попугая 51/85 2.2 Практика: Работаем с памятью
  • 118. Часть 2.3 Работаем с условными переходами 52/85 2.3 Практика: Работаем с условными переходами
  • 119. Branch prediction const int N = 32767; int[] sorted, unsorted; // random numbers [0..255] private static int Sum(int[] data) { int sum = 0; for (int i = 0; i < N; i++) if (data[i] >= 128) sum += data[i]; return sum; } [Benchmark] public int Sorted() { return Sum(sorted); } [Benchmark] public int Unsorted() { return Sum(unsorted); } 53/85 2.3 Практика: Работаем с условными переходами
  • 120. Branch prediction const int N = 32767; int[] sorted, unsorted; // random numbers [0..255] private static int Sum(int[] data) { int sum = 0; for (int i = 0; i < N; i++) if (data[i] >= 128) sum += data[i]; return sum; } [Benchmark] public int Sorted() { return Sum(sorted); } [Benchmark] public int Unsorted() { return Sum(unsorted); } Sorted Unsorted LegacyJIT-x86 1 попугай 7.4 попугая 53/85 2.3 Практика: Работаем с условными переходами
  • 121. Часть 2.4 Interface method dispatch 54/85 2.4 Практика: Interface method dispatch
  • 122. Интерфейсы private interface IFoo { double Inc(double x); } private class Foo1 : IFoo { public double Inc(double x) => x + 1; } private class Foo2 : IFoo { public double Inc(double x) => x + 1; } private double Run(IFoo foo) { double sum = 0; for (int i = 0; i < 1001; i++) sum += foo.Inc(0); return sum; } 55/85 2.4 Практика: Interface method dispatch
  • 123. Интерфейсы private interface IFoo { double Inc(double x); } private class Foo1 : IFoo { public double Inc(double x) => x + 1; } private class Foo2 : IFoo { public double Inc(double x) => x + 1; } private double Run(IFoo foo) { double sum = 0; for (int i = 0; i < 1001; i++) sum += foo.Inc(0); return sum; } [Benchmark] public double Run11() { var bar1 = new Foo1(); var bar2 = new Foo1(); return Run(bar1) + Run(bar2); } [Benchmark] public double Run12() { var bar1 = new Foo1(); var bar2 = new Foo2(); return Run(bar1) + Run(bar2); } 55/85 2.4 Практика: Interface method dispatch
  • 124. Интерфейсы private interface IFoo { double Inc(double x); } private class Foo1 : IFoo { public double Inc(double x) => x + 1; } private class Foo2 : IFoo { public double Inc(double x) => x + 1; } private double Run(IFoo foo) { double sum = 0; for (int i = 0; i < 1001; i++) sum += foo.Inc(0); return sum; } [Benchmark] public double Run11() { var bar1 = new Foo1(); var bar2 = new Foo1(); return Run(bar1) + Run(bar2); } [Benchmark] public double Run12() { var bar1 = new Foo1(); var bar2 = new Foo2(); return Run(bar1) + Run(bar2); } Run11 Run12 LegacyJIT-x64 1 попугай 1.25 попугая 55/85 2.4 Практика: Interface method dispatch
  • 125. Часть 2.5 Inlining 56/85 2.5 Практика: Inlining
  • 126. Исходники .NET Framework 57/85 2.5 Практика: Inlining
  • 127. Inlining — это сложно // mscorlib/system/decimal.cs,158 // Constructs a Decimal from an integer value. public Decimal(int value) { // JIT today can't inline methods that contains "starg" // opcode. For more details, see DevDiv Bugs 81184: // x86 JIT CQ: Removing the inline striction of "starg". int value_copy = value; if (value_copy >= 0) { flags = 0; } else { flags = SignMask; value_copy = -value_copy; } lo = value_copy; mid = 0; hi = 0; } 58/85 2.5 Практика: Inlining
  • 128. Проведём опыт [Benchmark] int Calc() => WithoutStarg(0x11) + WithStarg(0x12); int WithoutStarg(int value) => value; int WithStarg(int value) { if (value < 0) value = -value; return value; } 59/85 2.5 Практика: Inlining
  • 129. Проведём опыт [Benchmark] int Calc() => WithoutStarg(0x11) + WithStarg(0x12); int WithoutStarg(int value) => value; int WithStarg(int value) { if (value < 0) value = -value; return value; } LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64 1 попугай 0 попугаев 1 попугай 59/85 2.5 Практика: Inlining
  • 130. Как же так? LegacyJIT-x64 ; LegacyJIT-x64 mov ecx,23h ret 60/85 2.5 Практика: Inlining
  • 131. Как же так? LegacyJIT-x64 ; LegacyJIT-x64 mov ecx,23h ret RyuJIT-x64 // Inline expansion aborted due to opcode // [06] OP_starg.s in method // Program:WithStarg(int):int:this 60/85 2.5 Практика: Inlining
  • 132. Ещё одна загадка 61/85 2.5 Практика: Inlining
  • 134. Часть 2.6 Readonly fields 63/85 2.6 Практика: Readonly fields
  • 135. Почитаем блог Джона Скита 64/85 2.6 Практика: Readonly fields
  • 136. Проведём ещё один опыт public struct Int256 { private readonly long bits0, bits1, bits2, bits3; public Int256(long bits0, long bits1, long bits2, long bits3) { this.bits0 = bits0; this.bits1 = bits1; this.bits2 = bits2; this.bits3 = bits3; } public long Bits0 => bits0; public long Bits1 => bits1; public long Bits2 => bits2; public long Bits3 => bits3; } private Int256 a = new Int256(1L, 5L, 10L, 100L); private readonly Int256 b = new Int256(1L, 5L, 10L, 100L); [Benchmark] public long GetValue() => a.Bits0 + a.Bits1 + a.Bits2 + a.Bits3; [Benchmark] public long GetReadOnlyValue() => b.Bits0 + b.Bits1 + b.Bits2 + b.Bits3; 65/85 2.6 Практика: Readonly fields
  • 137. Проведём ещё один опыт public struct Int256 { private readonly long bits0, bits1, bits2, bits3; public Int256(long bits0, long bits1, long bits2, long bits3) { this.bits0 = bits0; this.bits1 = bits1; this.bits2 = bits2; this.bits3 = bits3; } public long Bits0 => bits0; public long Bits1 => bits1; public long Bits2 => bits2; public long Bits3 => bits3; } private Int256 a = new Int256(1L, 5L, 10L, 100L); private readonly Int256 b = new Int256(1L, 5L, 10L, 100L); [Benchmark] public long GetValue() => a.Bits0 + a.Bits1 + a.Bits2 + a.Bits3; [Benchmark] public long GetReadOnlyValue() => b.Bits0 + b.Bits1 + b.Bits2 + b.Bits3; LegacyJIT-x64 RyuJIT-x64 GetValue 1 попугай 1 попугай GetReadOnlyValue 6.2 попугая 7.6 попугая 65/85 2.6 Практика: Readonly fields
  • 138. Как же так? ; GetValue IL_0000: ldarg.0 IL_0001: ldflda valuetype Program::a IL_0006: call instance int64 Int256::get_Bits0() ; GetReadOnlyValue IL_0000: ldarg.0 IL_0001: ldfld valuetype Program::b IL_0006: stloc.0 IL_0007: ldloca.s 0 IL_0009: call instance int64 Int256::get_Bits0() См. также: Jon Skeet, Micro-optimization: the surprising inefficiency of readonly fields 66/85 2.6 Практика: Readonly fields
  • 139. Часть 2.7 SIMD 67/85 2.7 Практика: SIMD
  • 140. Поговорим про SIMD private struct MyVector { public float X, Y, Z, W; public MyVector(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MyVector operator *(MyVector left, MyVector right) { return new MyVector(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); } } private Vector4 vector1, vector2, vector3; private MyVector myVector1, myVector2, myVector3; [Benchmark] public void MyMul() => myVector3 = myVector1 * myVector2; [Benchmark] public void BclMul() => vector3 = vector1 * vector2; 68/85 2.7 Практика: SIMD
  • 141. Поговорим про SIMD private struct MyVector { public float X, Y, Z, W; public MyVector(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MyVector operator *(MyVector left, MyVector right) { return new MyVector(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); } } private Vector4 vector1, vector2, vector3; private MyVector myVector1, myVector2, myVector3; [Benchmark] public void MyMul() => myVector3 = myVector1 * myVector2; [Benchmark] public void BclMul() => vector3 = vector1 * vector2; LegacyJIT-x64 RyuJIT-x64 MyMul 34 попугая 5 попугаев BclMul 34 попугая 1 попугай 68/85 2.7 Практика: SIMD
  • 142. Как же так? ; LegacyJIT-x64 ; MyMul, BclMul ; ... movss xmm3,dword ptr [rsp+40h] mulss xmm3,dword ptr [rsp+30h] movss xmm2,dword ptr [rsp+44h] mulss xmm2,dword ptr [rsp+34h] movss xmm1,dword ptr [rsp+48h] mulss xmm1,dword ptr [rsp+38h] movss xmm0,dword ptr [rsp+4Ch] mulss xmm0,dword ptr [rsp+3Ch] xor eax,eax mov qword ptr [rsp],rax mov qword ptr [rsp+8],rax lea rax,[rsp] movss dword ptr [rax],xmm3 movss dword ptr [rax+4],xmm2 movss dword ptr [rax+8],xmm1 movss dword ptr [rax+0Ch],xmm0 ; ... ; RyuJIT-x64 ; MyMul ; ... vmulss xmm0,xmm0,xmm4 vmulss xmm1,xmm1,xmm5 vmulss xmm2,xmm2,xmm6 vmulss xmm3,xmm3,xmm7 ; ... ; BclMul vmovupd xmm0,xmmword ptr [rcx+8] vmovupd xmm1,xmmword ptr [rcx+18h] vmulps xmm0,xmm0,xmm1 vmovupd xmmword ptr [rcx+28h],xmm0 69/85 2.7 Практика: SIMD
  • 143. Часть 2.8 Constant folding 70/85 2.8 Практика: Constant folding
  • 144. Интересный issue на dotnet/coreclr 71/85 2.8 Практика: Constant folding
  • 145. Учимся извлекать корни double Sqrt13() => Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */ + Math.Sqrt(13); VS double Sqrt14() => Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */ + Math.Sqrt(13) + Math.Sqrt(14); 72/85 2.8 Практика: Constant folding
  • 146. Учимся извлекать корни double Sqrt13() => Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */ + Math.Sqrt(13); VS double Sqrt14() => Math.Sqrt(1) + Math.Sqrt(2) + Math.Sqrt(3) + /* ... */ + Math.Sqrt(13) + Math.Sqrt(14); RyuJIT-x64 Sqrt13 ≈ 93ns Sqrt14 0 ns 72/85 2.8 Практика: Constant folding
  • 147. Как же так? RyuJIT-x64, Sqrt13 vsqrtsd xmm0,xmm0,mmword ptr [7FF94F9E4D28h] vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D30h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D38h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D40h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D48h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D50h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D58h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D60h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D68h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D70h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D78h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D80h] vaddsd xmm0,xmm0,xmm1 vsqrtsd xmm1,xmm0,mmword ptr [7FF94F9E4D88h] vaddsd xmm0,xmm0,xmm1 ret 73/85 2.8 Практика: Constant folding
  • 148. Как же так? RyuJIT-x64, Sqrt14 vmovsd xmm0,qword ptr [7FF94F9C4C80h] ret 74/85 2.8 Практика: Constant folding
  • 149. Как же так? Большое дерево выражения * stmtExpr void (top level) (IL 0x000... ???) | /--* mathFN double sqrt | | --* dconst double 13.000000000000000 | /--* + double | | | /--* mathFN double sqrt | | | | --* dconst double 12.000000000000000 | | --* + double | | | /--* mathFN double sqrt | | | | --* dconst double 11.000000000000000 | | --* + double | | | /--* mathFN double sqrt | | | | --* dconst double 10.000000000000000 | | --* + double | | | /--* mathFN double sqrt | | | | --* dconst double 9.0000000000000000 | | --* + double | | | /--* mathFN double sqrt | | | | --* dconst double 8.0000000000000000 | | --* + double | | | /--* mathFN double sqrt | | | | --* dconst double 7.0000000000000000 | | --* + double | | | /--* mathFN double sqrt | | | | --* dconst double 6.0000000000000000 | | --* + double | | | /--* mathFN double sqrt | | | | --* dconst double 5.0000000000000000 // ... 75/85 2.8 Практика: Constant folding
  • 150. Как же так? Constant folding в действии N001 [000001] dconst 1.0000000000000000 => $c0 {DblCns[1.000000]} N002 [000002] mathFN => $c0 {DblCns[1.000000]} N003 [000003] dconst 2.0000000000000000 => $c1 {DblCns[2.000000]} N004 [000004] mathFN => $c2 {DblCns[1.414214]} N005 [000005] + => $c3 {DblCns[2.414214]} N006 [000006] dconst 3.0000000000000000 => $c4 {DblCns[3.000000]} N007 [000007] mathFN => $c5 {DblCns[1.732051]} N008 [000008] + => $c6 {DblCns[4.146264]} N009 [000009] dconst 4.0000000000000000 => $c7 {DblCns[4.000000]} N010 [000010] mathFN => $c1 {DblCns[2.000000]} N011 [000011] + => $c8 {DblCns[6.146264]} N012 [000012] dconst 5.0000000000000000 => $c9 {DblCns[5.000000]} N013 [000013] mathFN => $ca {DblCns[2.236068]} N014 [000014] + => $cb {DblCns[8.382332]} N015 [000015] dconst 6.0000000000000000 => $cc {DblCns[6.000000]} N016 [000016] mathFN => $cd {DblCns[2.449490]} N017 [000017] + => $ce {DblCns[10.831822]} N018 [000018] dconst 7.0000000000000000 => $cf {DblCns[7.000000]} N019 [000019] mathFN => $d0 {DblCns[2.645751]} N020 [000020] + => $d1 {DblCns[13.477573]} ... 76/85 2.8 Практика: Constant folding
  • 151. Часть 2.9 Instruction level parallelism 77/85 2.9 Практика: Instruction level parallelism
  • 152. Неожиданные perf-эффекты 1 Справедливо для RyuJIT RC, ныне пофикшено 78/85 2.9 Практика: Instruction level parallelism
  • 153. Задачка private double[] x = new double[11]; [Benchmark] public double Calc() { double sum = 0.0; for (int i = 1; i < x.Length; i++) sum += 1.0 / (i * i) * x[i]; return sum; } 79/85 2.9 Практика: Instruction level parallelism
  • 154. Задачка private double[] x = new double[11]; [Benchmark] public double Calc() { double sum = 0.0; for (int i = 1; i < x.Length; i++) sum += 1.0 / (i * i) * x[i]; return sum; } LegacyJIT-x64 RyuJIT-x641 Calc 1 попугай 2 попугая 1 RyuJIT RC 79/85 2.9 Практика: Instruction level parallelism
  • 155. Как же так? ; LegacyJIT-x64 ; eax = i mov eax,r8d ; eax = i*i imul eax,r8d ; xmm0=i*i cvtsi2sd xmm0,eax ; xmm1=1 movsd xmm1, mmword ptr [7FF9141145E0h] ; xmm1=1/(i*i) divsd xmm1,xmm0 ; xmm1=1/(i*i)*x[i] mulsd xmm1, mmword ptr [rdx+r9+10h] ; xmm1 = sum + 1/(i*i)*x[i] addsd xmm1,xmm2 ; sum = sum + 1/(i*i)*x[i] movapd xmm2,xmm1 ; RyuJIT-x64 ; r8d = i mov r8d,eax ; r8d = i*i imul r8d,eax ; xmm1=i*i vcvtsi2sd xmm1,xmm1,r8d ; xmm2=1 vmovsd xmm2, qword ptr [7FF9140E4398h] ; xmm2=1/(i*i) vdivsd xmm2,xmm2,xmm1 mov r8,rdx movsxd r9,eax ; xmm1 = 1/(i*i) vmovaps xmm1,xmm2 ; xmm1 = 1/(i*i)*x[i] vmulsd xmm1,xmm1, mmword ptr [r8+r9*8+10h] ; sum += 1/(i*i)*x[i] vaddsd xmm0,xmm0,xmm1 80/85 2.9 Практика: Instruction level parallelism
  • 158. Отказ от ответственности • Все представленные выводы и бенчмарки могут быть враньём 82/85 3. Заключение
  • 159. Отказ от ответственности • Все представленные выводы и бенчмарки могут быть враньём • Использование BenchmarkDotNet не делает ваш бенчмарк правильным 82/85 3. Заключение
  • 160. Отказ от ответственности • Все представленные выводы и бенчмарки могут быть враньём • Использование BenchmarkDotNet не делает ваш бенчмарк правильным • Использование самописных бенчмарков не делает выводы ложными 82/85 3. Заключение
  • 161. Сегодня мы узнали 83/85 3. Заключение
  • 162. Сегодня мы узнали • Бенчмаркинг и прочие замеры производительности — это сложно 83/85 3. Заключение
  • 163. Сегодня мы узнали • Бенчмаркинг и прочие замеры производительности — это сложно • Бенчмаркинг требует очень много сил, знаний, времени и нервов 83/85 3. Заключение
  • 164. Сегодня мы узнали • Бенчмаркинг и прочие замеры производительности — это сложно • Бенчмаркинг требует очень много сил, знаний, времени и нервов • Бенчмарк без анализа — плохой бенчмарк 83/85 3. Заключение
  • 165. Методическая литература Для успешных микробенчмарков нужно очень много знать: 84/85 3. Заключение