SlideShare ist ein Scribd-Unternehmen logo
1 von 69
Downloaden Sie, um offline zu lesen
Поговорим о микрооптимизациях .NET-приложений
Андрей Акиньшин, Энтерра
.NEXT 2015 Spb
1/50
Поговорим про общую теорию
Premature optimization is the root of all evil.
c Donald Ervin Knuth
2/50 Theory
С чего начать?
Хочу, чтобы программа работала быстрее!
3/50 Theory
С чего начать?
Хочу, чтобы программа работала быстрее!
• Хорошая архитектура
• Эффективные алгоритмы
• Правильные структуры данных
• Разумное использование памяти
• I/O
• Networking
• Кэш
• ...
3/50 Theory
С чего начать?
Хочу, чтобы программа работала быстрее!
• Хорошая архитектура
• Эффективные алгоритмы
• Правильные структуры данных
• Разумное использование памяти
• I/O
• Networking
• Кэш
• ...
Но мы не будем про это разговаривать...
3/50 Theory
Под что будем оптимизировать?
4/50 Theory
Под что будем оптимизировать?
• Версия CLR: CLR2? CLR4? CoreCLR? Mono?
• Версия ОС: Windows? Linux? MacOS?
• Версия JIT: x86? x64? RyuJIT?
• Версия GC: MS (какой CLR?)? Mono
(Boehm/Sgen)?
• Компиляция: JIT? NGen? Есть ли MPGO?
.NET Native?
• Разное железо: тысячи его.
4/50 Theory
Микробенчмаркинг — это сложно
Чтобы увеличить скорость работы, нужно сначала научиться её
измерять, а для этого:
5/50 Theory
Микробенчмаркинг — это сложно
Чтобы увеличить скорость работы, нужно сначала научиться её
измерять, а для этого:
• Запускаем бенчмарки в разных процессах.
• Делаем грамотный прогрев и подсчёт статистик.
• Проверяем разные окружения (и не забываем про RyuJIT).
• Побеждаем сайд-эффекты.
5/50 Theory
Микробенчмаркинг — это сложно
Чтобы увеличить скорость работы, нужно сначала научиться её
измерять, а для этого:
• Запускаем бенчмарки в разных процессах.
• Делаем грамотный прогрев и подсчёт статистик.
• Проверяем разные окружения (и не забываем про RyuJIT).
• Побеждаем сайд-эффекты.
• Понимаем устройство .NET, устройство ОС, устройство CPU и
устройство вселенной.
5/50 Theory
Микробенчмаркинг — это сложно
Чтобы увеличить скорость работы, нужно сначала научиться её
измерять, а для этого:
• Запускаем бенчмарки в разных процессах.
• Делаем грамотный прогрев и подсчёт статистик.
• Проверяем разные окружения (и не забываем про RyuJIT).
• Побеждаем сайд-эффекты.
• Понимаем устройство .NET, устройство ОС, устройство CPU и
устройство вселенной.
На правах рекламы:
https://github.com/PerfDotNet/BenchmarkDotNet/
https://www.nuget.org/packages/BenchmarkDotNet/
c Andrey Akinshin, Jon Skeet, Matt Warren
5/50 Theory
Микробенчмаркинг — это сложно
Чтобы увеличить скорость работы, нужно сначала научиться её
измерять, а для этого:
• Запускаем бенчмарки в разных процессах.
• Делаем грамотный прогрев и подсчёт статистик.
• Проверяем разные окружения (и не забываем про RyuJIT).
• Побеждаем сайд-эффекты.
• Понимаем устройство .NET, устройство ОС, устройство CPU и
устройство вселенной.
На правах рекламы:
https://github.com/PerfDotNet/BenchmarkDotNet/
https://www.nuget.org/packages/BenchmarkDotNet/
c Andrey Akinshin, Jon Skeet, Matt Warren
Сегодня мы будем проводить измерения в попугаях.
5/50 Theory
Поговорим про С#-компилятор и IL
You should always understand at least one layer
below what you are coding. c Lee Campbell
6/50 Compiler+IL
Поговорим про switch
switch (x)
{
case 0:
return 0;
case 1:
return 1;
case 2:
return 2;
case 3:
return 3;
}
return -1;
7/50 Compiler+IL
А что внутри?
; Фаза 1: switch
IL_0008: switch (IL_001f, IL_0021, IL_0023, IL_0025)
IL_001d: br.s IL_0027
; Фаза 2: cases
IL_001f: ldc.i4.0
IL_0020: ret
IL_0021: ldc.i4.1
IL_0022: ret
IL_0023: ldc.i4.2
IL_0024: ret
IL_0025: ldc.i4.3
IL_0026: ret
IL_0027: ldc.i4.m1
IL_0028: ret
8/50 Compiler+IL
Поговорим про switch
switch (x)
{
case 0:
return 0;
case 1000:
return 1000;
case 2000:
return 2000;
case 3000:
return 3000;
}
return -1;
9/50 Compiler+IL
А что внутри?
; Фаза 1: switch
IL_0007: ldloc.0
IL_0008: ldc.i4 1000
IL_000d: bgt.s IL_001c
IL_000f: ldloc.0
IL_0010: brfalse.s IL_002e
IL_0012: ldloc.0
IL_0013: ldc.i4 1000
IL_0018: beq.s IL_0030
IL_001a: br.s IL_0042
IL_001c: ldloc.0
IL_001d: ldc.i4 2000
IL_0022: beq.s IL_0036
IL_0024: ldloc.0
IL_0025: ldc.i4 3000
IL_002a: beq.s IL_003c
IL_002c: br.s IL_0042
; Фаза 2: cases
IL_002e: ldc.i4.0
IL_002f: ret
IL_0030: ldc.i4 1000
IL_0035: ret
IL_0036: ldc.i4 2000
IL_003b: ret
IL_003c: ldc.i4 3000
IL_0041: ret
IL_0042: ldc.i4.m1
IL_0043: ret
10/50 Compiler+IL
Поговорим про switch
switch (x)
{
case "a":
return "A";
case "b":
return "B";
case "c":
return "C";
case "d":
return "D";
}
return "";
11/50 Compiler+IL
А что внутри?
; Фаза 1: switch
IL_0007: ldloc.0
IL_0008: ldstr "a"
IL_000d: call bool String::op_Equality
IL_0012: brtrue.s IL_003d
IL_0014: ldloc.0
IL_0015: ldstr "b"
IL_001a: call bool String::op_Equality
IL_001f: brtrue.s IL_0043
IL_0021: ldloc.0
IL_0022: ldstr "c"
IL_0027: call bool String::op_Equality
IL_002c: brtrue.s IL_0049
IL_002e: ldloc.0
IL_002f: ldstr "d"
IL_0034: call bool String::op_Equality
IL_0039: brtrue.s IL_004f
IL_003b: br.s IL_0055
; Фаза 2: cases
IL_003d: ldstr "A"
IL_0042: ret
IL_0043: ldstr "B"
IL_0048: ret
IL_0049: ldstr "C"
IL_004e: ret
IL_004f: ldstr "D"
IL_0054: ret
IL_0055: ldstr ""
IL_005a: ret
12/50 Compiler+IL
Поговорим про switch
switch (x)
{
case "a":
return "A";
case "b":
return "B";
case "c":
return "C";
case "d":
return "D";
case "e":
return "E";
case "f":
return "F";
case "g":
return "G";
}
return "";
13/50 Compiler+IL
А что внутри?
; Старый компилятор
; Фаза 0: Dictionary<string, string>
IL_000d: volatile.
IL_000f: ldsfld class
Dictionary<string, int32>
IL_0014: brtrue.s IL_0077
IL_0016: ldc.i4.7
IL_0017: newobj instance void class
Dictionary<string, int32>::.ctor
IL_001c: dup
IL_001d: ldstr "a"
IL_0022: ldc.i4.0
IL_0023: call instance void class
Dictionary<string, int32>::Add
IL_0028: dup
IL_0029: ldstr "b"
IL_002e: ldc.i4.1
IL_0023: call instance void class
Dictionary<string, int32>::Add
IL_0034: dup
IL_0035: ldstr "c"
; ...
; Фаза 1:
IL_0088: ldloc.1
IL_0089: switch (
IL_00ac, IL_00b2, IL_00b8,
IL_00be, IL_00c4, IL_00ca, IL_00d0)
IL_00aa: br.s IL_00d6
; Фаза 2: cases
IL_00ac: ldstr "A"
IL_00b1: ret
IL_00b2: ldstr "B"
IL_00b7: ret
IL_00b8: ldstr "C"
IL_00bd: ret
IL_00be: ldstr "D"
IL_00c3: ret
IL_00c4: ldstr "E"
IL_00c9: ret
IL_00ca: ldstr "F"
IL_00cf: ret
IL_00d0: ldstr "G"
IL_00d5: ret
IL_00d6: ldstr ""
IL_00db: ret
14/50 Compiler+IL
А что внутри?
// Roslyn
// Фаза 1: ComputeStringHash
uint num = ComputeStringHash(x);
// Фаза 2: Бинарный поиск
if (num <= 3792446982u)
if (num != 3758891744u)
if (num != 3775669363u)
if (num == 3792446982u)
if (text == "g")
return "G";
else if (text == "d")
return "D";
else if (text == "e")
return "E";
else if (num <= 3826002220u)
if (num != 3809224601u)
if (num == 3826002220u)
if (text == "a")
return "A";
else if (text == "f")
return "F";
else if (num != 3859557458u)
if (num == 3876335077u)
if (text == "b")
return "B";
else if (text == "c")
return "C";
return "";
15/50 Compiler+IL
История
16/50 Compiler+IL
Поговорим про 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;
17/50 Compiler+IL
Поговорим про 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 попугая
17/50 Compiler+IL
Как же так?
; 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
18/50 Compiler+IL
Поговорим про JIT и ASM
19/50 JIT+ASM
Наши новые друзья
• COMPLUS_JitDump
• COMPLUS_JitDisasm
• COMPLUS_JitDiffableDasm
• COMPLUS_JitGCDump
• COMPLUS_JitUnwindDump
• COMPLUS_JitEHDump
• COMPLUS_JitTimeLogFile
• COMPLUS_JitTimeLogCsv
См. также:
Book of the Runtime, JIT Compiler Structure
20/50 JIT+ASM
Array bound check elimination
const int N = 11;
int[] a = new int[N];
for (int i = 0; i < N; i++)
sum += a[i];
VS
for (int i = 0; i < a.Length; i++)
sum += a[i];
21/50 JIT+ASM
Array bound check elimination
const int N = 11;
int[] a = new int[N];
for (int i = 0; i < N; i++)
sum += a[i];
VS
for (int i = 0; i < a.Length; i++)
sum += a[i];
LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64
N 1.3 попугая 1 попугай 1.6 попугая
a.Length 1.2 попугая 1 попугай 1.5 попугая
21/50 JIT+ASM
Как же так?
LegacyJIT-x86
; LegacyJIT-x86, N
; ...
Loop:
; if i >= a.Length
cmp eax,edx
; then throw Exception
jae 004635F9
; sum += a[i]
add ecx,dword ptr [esi+eax*4+8]
; i++
inc eax
; if i < N
cmp eax,0Bh
; then continue
jl Loop
; LegacyJIT-x86, a.Length
; ...
; ...
; ...
; ...
; ...
Loop:
; sum += a[i]
add ecx,dword ptr [esi+edx*4+8]
; i++
inc edx
; if i < a.Length
cmp eax,edx
; then continue
jg Loop
22/50 JIT+ASM
Как же так?
LegacyJIT-x64
; LegacyJIT-x64, N
; ...
Loop:
; eax = x[i]
mov eax,dword ptr [r8+rcx+10h]
; sum += a[i]
add edx,eax
; i++
add rcx,4
; if i < N
cmp rcx,2Ch
; then continue
jl Loop
; LegacyJIT-x64, a.Length
; ...
Loop:
; eax = x[i]
mov eax,dword ptr [r9+r8+10h]
; sum += a[i]
add ecx,eax
; i++
inc edx
add r8,4
; if i < N
cmp edx,r10d
; then continue
jl Loop
23/50 JIT+ASM
Как же так?
RyuJIT-x64
; RyuJIT-x64, N
Loop:
; r8 = a.Length
mov r8d,dword ptr [rax+8]
; if i >= a.Length
cmp ecx,r8d
; then throw exception
jae 00007FF94F9B46B4
; r8 = i
movsxd r8,ecx
; r8 = a[i]
mov r8d,dword ptr [rax+r8*4+10h]
; sum += a[i]
add edx,r8d
; i++
inc ecx
; if i < N
cmp ecx,0Bh
; then continue
jl Loop
; RyuJIT-x64, a.Length
; ...
; ...
; ...
; ...
; ...
; ...
Loop:
; r9 = i
movsxd r9,ecx
; r9 = x[i]
mov r9d,dword ptr [rax+r9*4+10h]
; sum += x[i]
add edx,r9d
; i++
inc ecx
; if i < a.Length
cmp r8d,ecx
; then continue
jg Loop
24/50 JIT+ASM
История
25/50 JIT+ASM
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;
}
26/50 JIT+ASM
Inlining
[Benchmark]
int Calc() => WithoutStarg(0x11) + WithStarg(0x12);
int WithoutStarg(int value) => value;
int WithStarg(int value)
{
if (value < 0)
value = -value;
return value;
}
27/50 JIT+ASM
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 попугай
27/50 JIT+ASM
Как же так?
LegacyJIT-x64
; LegacyJIT-x64
mov ecx,23h
ret
28/50 JIT+ASM
Как же так?
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
28/50 JIT+ASM
История
29/50 JIT+ASM
Loop unrolling
int sum = 0;
for (int i = 0; i < 1024; i++)
sum += i;
30/50 JIT+ASM
Loop unrolling
int sum = 0;
for (int i = 0; i < 1024; i++)
sum += i;
LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64
1.0 попугай 0.9 попугая 1.0 попугай
30/50 JIT+ASM
Как же так?
LegacyJIT-x64
xor ecx,ecx
mov edx,1
Loop:
lea eax,[rdx-1]
add ecx,eax ; sum += (i)
add ecx,edx ; sum += (i+1)
lea eax,[rdx+1]
add ecx,eax ; sum += (i+2)
lea eax,[rdx+2]
add ecx,eax ; sum += (i+3)
add edx,4 ; i += 4
cmp edx,401h
jl Loop
31/50 JIT+ASM
Поговорим про параллелизм
32/50 Parallelism
Поговорим про параллелизм
Но не будем разговаривать про потоки, задачи,
PLINQ и прочий мультитрединг...
32/50 Parallelism
Поговорим про 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;
33/50 Parallelism
Поговорим про 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 попугай
33/50 Parallelism
Как же так?
; 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
34/50 Parallelism
Думаем про ASM
double x = /* ... */ ;
double a = x + 1;
double b = x * 2;
double c = Math.Sqrt(x);
35/50 Parallelism
Думаем про ASM
double x = /* ... */ ;
double a = x + 1;
double b = x * 2;
double c = Math.Sqrt(x);
LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64
(x87 FPU) (SSE2) (AVX)
x + 1 faddp addsd vaddsd
x * 2 fmul mulsd vmulsd
Sqrt(X) fsqrt sqrtsd vsqrtsd
35/50 Parallelism
История
36/50 Parallelism
Задачка
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);
37/50 Parallelism
Задачка
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-x641
Sqrt13 40 попугаев
Sqrt14 1 попугай
1
RyuJIT RC
37/50 Parallelism
Как же так?
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
38/50 Parallelism
Как же так?
RyuJIT-x64, Sqrt14
vmovsd xmm0,qword ptr [7FF94F9C4C80h]
ret
39/50 Parallelism
Как же так?
Большое дерево выражения
* 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
// ...
40/50 Parallelism
Как же так?
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]}
...
41/50 Parallelism
Instruction-level parallelism
42/50 Parallelism
Instruction-level parallelism
public void Parallel()
{
a++; b++; c++; d++;
}
VS
public void Sequential()
{
a++; a++; a++; a++;
}
43/50 Parallelism
Instruction-level parallelism
public void Parallel()
{
a++; b++; c++; d++;
}
VS
public void Sequential()
{
a++; a++; a++; a++;
}
LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64
Parallel 1.2 попугая 1 попугай 1.2 попугая
Sequential 6.0 попугая 5.2 попугая 2.2 попугая
43/50 Parallelism
Задачка
public void Parallel()
{
x[0]++; x[1]++; x[2]++; x[3]++;
}
VS
public void Sequential()
{
x[0]++; x[0]++; x[0]++; x[0]++;
}
44/50 Parallelism
Задачка
public void Parallel()
{
x[0]++; x[1]++; x[2]++; x[3]++;
}
VS
public void Sequential()
{
x[0]++; x[0]++; x[0]++; x[0]++;
}
LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64
Parallel 1.2 попугая 1 попугай 1 попугай
Sequential 3.3 попугая 2.7 попугая 1 попугай
44/50 Parallelism
Как же так?
; LegacyJIT-x64, Sequential
; x[0]++;
mov rdx,qword ptr [rcx+8]
mov rax,qword ptr [rdx+8]
test rax,rax
jbe IndexOutOfRangeException
inc dword ptr [rdx+10h]
; x[0]++;
mov rdx,qword ptr [rcx+8]
mov rax,qword ptr [rdx+8]
test rax,rax
jbe IndexOutOfRangeException
inc dword ptr [rdx+10h]
; ...
; RyuJIT-x64, Sequential
; x[0]++;
mov rax,qword ptr [rcx+8]
cmp dword ptr [rax+8],0
jbe IndexOutOfRangeException
add rax,10h
mov rdx,rax
mov ecx,dword ptr [rdx]
inc ecx
mov dword ptr [rdx],ecx
; x[0]++;
mov rdx,rax
inc ecx
mov dword ptr [rdx],ecx
; ...
45/50 Parallelism
История
46/50 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;
}
47/50 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
47/50 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
См. также: https://github.com/dotnet/coreclr/issues/993
48/50 Parallelism
Методическая литература
Для успешных микрооптимизаций нужно очень много знать:
49/50
Вопросы?
Андрей Акиньшин, Энтерра
http://aakinshin.net
https://github.com/AndreyAkinshin
https://twitter.com/andrey_akinshin
andrey.akinshin@gmail.com
50/50

Weitere ähnliche Inhalte

Was ist angesagt?

20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
Computer Science Club
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Mikhail Kurnosov
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
Eugeniy Tyumentcev
 

Was ist angesagt? (19)

Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
Игорь Кудрин, «Используем неизменяемые данные и создаем качественный код»
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
 
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
 
Сложности микробенчмаркинга
Сложности микробенчмаркингаСложности микробенчмаркинга
Сложности микробенчмаркинга
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о Boost
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
хитрости выведения типов
хитрости выведения типовхитрости выведения типов
хитрости выведения типов
 
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
C++ CoreHard Autumn 2018. Полезный constexpr - Антон ПолухинC++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
 
Unsafe: to be or to be removed?
Unsafe: to be or to be removed?Unsafe: to be or to be removed?
Unsafe: to be or to be removed?
 
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионаловПолухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
 
разработка серверов и серверных приложений лекция №2
разработка серверов и серверных приложений лекция №2разработка серверов и серверных приложений лекция №2
разработка серверов и серверных приложений лекция №2
 
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 
Догнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_castДогнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_cast
 
Python&Printer / Андрей Пучко / penta.by
Python&Printer / Андрей Пучко / penta.byPython&Printer / Андрей Пучко / penta.by
Python&Printer / Андрей Пучко / penta.by
 
Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++
 

Andere mochten auch

Evaluation question 3 – what have you learnt
Evaluation question 3 – what have you learntEvaluation question 3 – what have you learnt
Evaluation question 3 – what have you learnt
Tomcull18
 
CornerStone Volume 16 Issue 2
CornerStone Volume 16 Issue 2CornerStone Volume 16 Issue 2
CornerStone Volume 16 Issue 2
Ryan Vinson
 
Business System Development
Business System DevelopmentBusiness System Development
Business System Development
steffiann88
 

Andere mochten auch (20)

Поговорим о различных версиях .NET
Поговорим о различных версиях .NETПоговорим о различных версиях .NET
Поговорим о различных версиях .NET
 
.NET 2015: Будущее рядом
.NET 2015: Будущее рядом.NET 2015: Будущее рядом
.NET 2015: Будущее рядом
 
Сборка мусора в .NET
Сборка мусора в .NETСборка мусора в .NET
Сборка мусора в .NET
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложений
 
Низкоуровневые оптимизации .NET-приложений
Низкоуровневые оптимизации .NET-приложенийНизкоуровневые оптимизации .NET-приложений
Низкоуровневые оптимизации .NET-приложений
 
Phd presentation
Phd presentationPhd presentation
Phd presentation
 
Практические приёмы оптимизации .NET-приложений
Практические приёмы оптимизации .NET-приложенийПрактические приёмы оптимизации .NET-приложений
Практические приёмы оптимизации .NET-приложений
 
Что нам готовит грядущий C#7?
Что нам готовит грядущий C#7?Что нам готовит грядущий C#7?
Что нам готовит грядущий C#7?
 
Подружили CLR и JVM в Project Rider
Подружили CLR и JVM в Project RiderПодружили CLR и JVM в Project Rider
Подружили CLR и JVM в Project Rider
 
Теория и практика .NET-бенчмаркинга (02.11.2016, Екатеринбург)
Теория и практика .NET-бенчмаркинга (02.11.2016, Екатеринбург)Теория и практика .NET-бенчмаркинга (02.11.2016, Екатеринбург)
Теория и практика .NET-бенчмаркинга (02.11.2016, Екатеринбург)
 
Let’s talk about microbenchmarking
Let’s talk about microbenchmarkingLet’s talk about microbenchmarking
Let’s talk about microbenchmarking
 
Основы работы с Git
Основы работы с GitОсновы работы с Git
Основы работы с Git
 
Теория и практика .NET-бенчмаркинга (25.01.2017, Москва)
 Теория и практика .NET-бенчмаркинга (25.01.2017, Москва) Теория и практика .NET-бенчмаркинга (25.01.2017, Москва)
Теория и практика .NET-бенчмаркинга (25.01.2017, Москва)
 
Evaluation question 3 – what have you learnt
Evaluation question 3 – what have you learntEvaluation question 3 – what have you learnt
Evaluation question 3 – what have you learnt
 
Un mes, una estampa. Ver Sevilla.
Un mes, una estampa. Ver Sevilla.Un mes, una estampa. Ver Sevilla.
Un mes, una estampa. Ver Sevilla.
 
CornerStone Volume 16 Issue 2
CornerStone Volume 16 Issue 2CornerStone Volume 16 Issue 2
CornerStone Volume 16 Issue 2
 
Guia de-precios-compugreiff-5-de-septiembre-de-2015
Guia de-precios-compugreiff-5-de-septiembre-de-2015Guia de-precios-compugreiff-5-de-septiembre-de-2015
Guia de-precios-compugreiff-5-de-septiembre-de-2015
 
Ppsileshare
PpsilesharePpsileshare
Ppsileshare
 
Business System Development
Business System DevelopmentBusiness System Development
Business System Development
 
Cuidatge guía de uso vr. 2
Cuidatge guía de uso vr. 2Cuidatge guía de uso vr. 2
Cuidatge guía de uso vr. 2
 

Ähnlich wie Поговорим о микрооптимизациях .NET-приложений

Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Platonov Sergey
 
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Ivan Tsyganov
 
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016
Кирилл Толкачёв
 

Ähnlich wie Поговорим о микрооптимизациях .NET-приложений (20)

Python и Cython
Python и CythonPython и Cython
Python и Cython
 
Jbreak 2016: Твой личный Spring Boot Starter
Jbreak 2016: Твой личный Spring Boot StarterJbreak 2016: Твой личный Spring Boot Starter
Jbreak 2016: Твой личный Spring Boot Starter
 
SECON'2016. Чубарь Алексей, Мобильные грабли Unity
SECON'2016. Чубарь Алексей, Мобильные грабли UnitySECON'2016. Чубарь Алексей, Мобильные грабли Unity
SECON'2016. Чубарь Алексей, Мобильные грабли Unity
 
Архитектура в Agile: слабая связность
Архитектура в Agile: слабая связностьАрхитектура в Agile: слабая связность
Архитектура в Agile: слабая связность
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
 
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
Intel IPP Samples for Windows - работа над ошибками
Intel IPP Samples for Windows - работа над ошибкамиIntel IPP Samples for Windows - работа над ошибками
Intel IPP Samples for Windows - работа над ошибками
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?
 
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
 
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
 
Сенцов Сергей "Приемы оптимизаций Desktop приложений"
Сенцов Сергей "Приемы оптимизаций Desktop приложений"Сенцов Сергей "Приемы оптимизаций Desktop приложений"
Сенцов Сергей "Приемы оптимизаций Desktop приложений"
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016
 
Как не подавиться большим старым проектом. Юрий Минаев ➠ CoreHard Autumn 2019
Как не подавиться большим старым проектом. Юрий Минаев ➠  CoreHard Autumn 2019Как не подавиться большим старым проектом. Юрий Минаев ➠  CoreHard Autumn 2019
Как не подавиться большим старым проектом. Юрий Минаев ➠ CoreHard Autumn 2019
 
Как не подавиться большим старым проектом
Как не подавиться большим старым проектомКак не подавиться большим старым проектом
Как не подавиться большим старым проектом
 
Static code analysis of the projects built on Unreal Engine
Static code analysis of the projects built on Unreal EngineStatic code analysis of the projects built on Unreal Engine
Static code analysis of the projects built on Unreal Engine
 
Статический анализ кода проектов, построенных на движке Unreal Engine
Статический анализ кода проектов, построенных на движке Unreal EngineСтатический анализ кода проектов, построенных на движке Unreal Engine
Статический анализ кода проектов, построенных на движке Unreal Engine
 
Java осень 2014 занятие 6
Java осень 2014 занятие 6Java осень 2014 занятие 6
Java осень 2014 занятие 6
 
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья ШишковC++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
 

Поговорим о микрооптимизациях .NET-приложений

  • 1. Поговорим о микрооптимизациях .NET-приложений Андрей Акиньшин, Энтерра .NEXT 2015 Spb 1/50
  • 2. Поговорим про общую теорию Premature optimization is the root of all evil. c Donald Ervin Knuth 2/50 Theory
  • 3. С чего начать? Хочу, чтобы программа работала быстрее! 3/50 Theory
  • 4. С чего начать? Хочу, чтобы программа работала быстрее! • Хорошая архитектура • Эффективные алгоритмы • Правильные структуры данных • Разумное использование памяти • I/O • Networking • Кэш • ... 3/50 Theory
  • 5. С чего начать? Хочу, чтобы программа работала быстрее! • Хорошая архитектура • Эффективные алгоритмы • Правильные структуры данных • Разумное использование памяти • I/O • Networking • Кэш • ... Но мы не будем про это разговаривать... 3/50 Theory
  • 6. Под что будем оптимизировать? 4/50 Theory
  • 7. Под что будем оптимизировать? • Версия CLR: CLR2? CLR4? CoreCLR? Mono? • Версия ОС: Windows? Linux? MacOS? • Версия JIT: x86? x64? RyuJIT? • Версия GC: MS (какой CLR?)? Mono (Boehm/Sgen)? • Компиляция: JIT? NGen? Есть ли MPGO? .NET Native? • Разное железо: тысячи его. 4/50 Theory
  • 8. Микробенчмаркинг — это сложно Чтобы увеличить скорость работы, нужно сначала научиться её измерять, а для этого: 5/50 Theory
  • 9. Микробенчмаркинг — это сложно Чтобы увеличить скорость работы, нужно сначала научиться её измерять, а для этого: • Запускаем бенчмарки в разных процессах. • Делаем грамотный прогрев и подсчёт статистик. • Проверяем разные окружения (и не забываем про RyuJIT). • Побеждаем сайд-эффекты. 5/50 Theory
  • 10. Микробенчмаркинг — это сложно Чтобы увеличить скорость работы, нужно сначала научиться её измерять, а для этого: • Запускаем бенчмарки в разных процессах. • Делаем грамотный прогрев и подсчёт статистик. • Проверяем разные окружения (и не забываем про RyuJIT). • Побеждаем сайд-эффекты. • Понимаем устройство .NET, устройство ОС, устройство CPU и устройство вселенной. 5/50 Theory
  • 11. Микробенчмаркинг — это сложно Чтобы увеличить скорость работы, нужно сначала научиться её измерять, а для этого: • Запускаем бенчмарки в разных процессах. • Делаем грамотный прогрев и подсчёт статистик. • Проверяем разные окружения (и не забываем про RyuJIT). • Побеждаем сайд-эффекты. • Понимаем устройство .NET, устройство ОС, устройство CPU и устройство вселенной. На правах рекламы: https://github.com/PerfDotNet/BenchmarkDotNet/ https://www.nuget.org/packages/BenchmarkDotNet/ c Andrey Akinshin, Jon Skeet, Matt Warren 5/50 Theory
  • 12. Микробенчмаркинг — это сложно Чтобы увеличить скорость работы, нужно сначала научиться её измерять, а для этого: • Запускаем бенчмарки в разных процессах. • Делаем грамотный прогрев и подсчёт статистик. • Проверяем разные окружения (и не забываем про RyuJIT). • Побеждаем сайд-эффекты. • Понимаем устройство .NET, устройство ОС, устройство CPU и устройство вселенной. На правах рекламы: https://github.com/PerfDotNet/BenchmarkDotNet/ https://www.nuget.org/packages/BenchmarkDotNet/ c Andrey Akinshin, Jon Skeet, Matt Warren Сегодня мы будем проводить измерения в попугаях. 5/50 Theory
  • 13. Поговорим про С#-компилятор и IL You should always understand at least one layer below what you are coding. c Lee Campbell 6/50 Compiler+IL
  • 14. Поговорим про switch switch (x) { case 0: return 0; case 1: return 1; case 2: return 2; case 3: return 3; } return -1; 7/50 Compiler+IL
  • 15. А что внутри? ; Фаза 1: switch IL_0008: switch (IL_001f, IL_0021, IL_0023, IL_0025) IL_001d: br.s IL_0027 ; Фаза 2: cases IL_001f: ldc.i4.0 IL_0020: ret IL_0021: ldc.i4.1 IL_0022: ret IL_0023: ldc.i4.2 IL_0024: ret IL_0025: ldc.i4.3 IL_0026: ret IL_0027: ldc.i4.m1 IL_0028: ret 8/50 Compiler+IL
  • 16. Поговорим про switch switch (x) { case 0: return 0; case 1000: return 1000; case 2000: return 2000; case 3000: return 3000; } return -1; 9/50 Compiler+IL
  • 17. А что внутри? ; Фаза 1: switch IL_0007: ldloc.0 IL_0008: ldc.i4 1000 IL_000d: bgt.s IL_001c IL_000f: ldloc.0 IL_0010: brfalse.s IL_002e IL_0012: ldloc.0 IL_0013: ldc.i4 1000 IL_0018: beq.s IL_0030 IL_001a: br.s IL_0042 IL_001c: ldloc.0 IL_001d: ldc.i4 2000 IL_0022: beq.s IL_0036 IL_0024: ldloc.0 IL_0025: ldc.i4 3000 IL_002a: beq.s IL_003c IL_002c: br.s IL_0042 ; Фаза 2: cases IL_002e: ldc.i4.0 IL_002f: ret IL_0030: ldc.i4 1000 IL_0035: ret IL_0036: ldc.i4 2000 IL_003b: ret IL_003c: ldc.i4 3000 IL_0041: ret IL_0042: ldc.i4.m1 IL_0043: ret 10/50 Compiler+IL
  • 18. Поговорим про switch switch (x) { case "a": return "A"; case "b": return "B"; case "c": return "C"; case "d": return "D"; } return ""; 11/50 Compiler+IL
  • 19. А что внутри? ; Фаза 1: switch IL_0007: ldloc.0 IL_0008: ldstr "a" IL_000d: call bool String::op_Equality IL_0012: brtrue.s IL_003d IL_0014: ldloc.0 IL_0015: ldstr "b" IL_001a: call bool String::op_Equality IL_001f: brtrue.s IL_0043 IL_0021: ldloc.0 IL_0022: ldstr "c" IL_0027: call bool String::op_Equality IL_002c: brtrue.s IL_0049 IL_002e: ldloc.0 IL_002f: ldstr "d" IL_0034: call bool String::op_Equality IL_0039: brtrue.s IL_004f IL_003b: br.s IL_0055 ; Фаза 2: cases IL_003d: ldstr "A" IL_0042: ret IL_0043: ldstr "B" IL_0048: ret IL_0049: ldstr "C" IL_004e: ret IL_004f: ldstr "D" IL_0054: ret IL_0055: ldstr "" IL_005a: ret 12/50 Compiler+IL
  • 20. Поговорим про switch switch (x) { case "a": return "A"; case "b": return "B"; case "c": return "C"; case "d": return "D"; case "e": return "E"; case "f": return "F"; case "g": return "G"; } return ""; 13/50 Compiler+IL
  • 21. А что внутри? ; Старый компилятор ; Фаза 0: Dictionary<string, string> IL_000d: volatile. IL_000f: ldsfld class Dictionary<string, int32> IL_0014: brtrue.s IL_0077 IL_0016: ldc.i4.7 IL_0017: newobj instance void class Dictionary<string, int32>::.ctor IL_001c: dup IL_001d: ldstr "a" IL_0022: ldc.i4.0 IL_0023: call instance void class Dictionary<string, int32>::Add IL_0028: dup IL_0029: ldstr "b" IL_002e: ldc.i4.1 IL_0023: call instance void class Dictionary<string, int32>::Add IL_0034: dup IL_0035: ldstr "c" ; ... ; Фаза 1: IL_0088: ldloc.1 IL_0089: switch ( IL_00ac, IL_00b2, IL_00b8, IL_00be, IL_00c4, IL_00ca, IL_00d0) IL_00aa: br.s IL_00d6 ; Фаза 2: cases IL_00ac: ldstr "A" IL_00b1: ret IL_00b2: ldstr "B" IL_00b7: ret IL_00b8: ldstr "C" IL_00bd: ret IL_00be: ldstr "D" IL_00c3: ret IL_00c4: ldstr "E" IL_00c9: ret IL_00ca: ldstr "F" IL_00cf: ret IL_00d0: ldstr "G" IL_00d5: ret IL_00d6: ldstr "" IL_00db: ret 14/50 Compiler+IL
  • 22. А что внутри? // Roslyn // Фаза 1: ComputeStringHash uint num = ComputeStringHash(x); // Фаза 2: Бинарный поиск if (num <= 3792446982u) if (num != 3758891744u) if (num != 3775669363u) if (num == 3792446982u) if (text == "g") return "G"; else if (text == "d") return "D"; else if (text == "e") return "E"; else if (num <= 3826002220u) if (num != 3809224601u) if (num == 3826002220u) if (text == "a") return "A"; else if (text == "f") return "F"; else if (num != 3859557458u) if (num == 3876335077u) if (text == "b") return "B"; else if (text == "c") return "C"; return ""; 15/50 Compiler+IL
  • 24. Поговорим про 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; 17/50 Compiler+IL
  • 25. Поговорим про 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 попугая 17/50 Compiler+IL
  • 26. Как же так? ; 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 18/50 Compiler+IL
  • 27. Поговорим про JIT и ASM 19/50 JIT+ASM
  • 28. Наши новые друзья • COMPLUS_JitDump • COMPLUS_JitDisasm • COMPLUS_JitDiffableDasm • COMPLUS_JitGCDump • COMPLUS_JitUnwindDump • COMPLUS_JitEHDump • COMPLUS_JitTimeLogFile • COMPLUS_JitTimeLogCsv См. также: Book of the Runtime, JIT Compiler Structure 20/50 JIT+ASM
  • 29. Array bound check elimination const int N = 11; int[] a = new int[N]; for (int i = 0; i < N; i++) sum += a[i]; VS for (int i = 0; i < a.Length; i++) sum += a[i]; 21/50 JIT+ASM
  • 30. Array bound check elimination const int N = 11; int[] a = new int[N]; for (int i = 0; i < N; i++) sum += a[i]; VS for (int i = 0; i < a.Length; i++) sum += a[i]; LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64 N 1.3 попугая 1 попугай 1.6 попугая a.Length 1.2 попугая 1 попугай 1.5 попугая 21/50 JIT+ASM
  • 31. Как же так? LegacyJIT-x86 ; LegacyJIT-x86, N ; ... Loop: ; if i >= a.Length cmp eax,edx ; then throw Exception jae 004635F9 ; sum += a[i] add ecx,dword ptr [esi+eax*4+8] ; i++ inc eax ; if i < N cmp eax,0Bh ; then continue jl Loop ; LegacyJIT-x86, a.Length ; ... ; ... ; ... ; ... ; ... Loop: ; sum += a[i] add ecx,dword ptr [esi+edx*4+8] ; i++ inc edx ; if i < a.Length cmp eax,edx ; then continue jg Loop 22/50 JIT+ASM
  • 32. Как же так? LegacyJIT-x64 ; LegacyJIT-x64, N ; ... Loop: ; eax = x[i] mov eax,dword ptr [r8+rcx+10h] ; sum += a[i] add edx,eax ; i++ add rcx,4 ; if i < N cmp rcx,2Ch ; then continue jl Loop ; LegacyJIT-x64, a.Length ; ... Loop: ; eax = x[i] mov eax,dword ptr [r9+r8+10h] ; sum += a[i] add ecx,eax ; i++ inc edx add r8,4 ; if i < N cmp edx,r10d ; then continue jl Loop 23/50 JIT+ASM
  • 33. Как же так? RyuJIT-x64 ; RyuJIT-x64, N Loop: ; r8 = a.Length mov r8d,dword ptr [rax+8] ; if i >= a.Length cmp ecx,r8d ; then throw exception jae 00007FF94F9B46B4 ; r8 = i movsxd r8,ecx ; r8 = a[i] mov r8d,dword ptr [rax+r8*4+10h] ; sum += a[i] add edx,r8d ; i++ inc ecx ; if i < N cmp ecx,0Bh ; then continue jl Loop ; RyuJIT-x64, a.Length ; ... ; ... ; ... ; ... ; ... ; ... Loop: ; r9 = i movsxd r9,ecx ; r9 = x[i] mov r9d,dword ptr [rax+r9*4+10h] ; sum += x[i] add edx,r9d ; i++ inc ecx ; if i < a.Length cmp r8d,ecx ; then continue jg Loop 24/50 JIT+ASM
  • 35. 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; } 26/50 JIT+ASM
  • 36. Inlining [Benchmark] int Calc() => WithoutStarg(0x11) + WithStarg(0x12); int WithoutStarg(int value) => value; int WithStarg(int value) { if (value < 0) value = -value; return value; } 27/50 JIT+ASM
  • 37. 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 попугай 27/50 JIT+ASM
  • 38. Как же так? LegacyJIT-x64 ; LegacyJIT-x64 mov ecx,23h ret 28/50 JIT+ASM
  • 39. Как же так? 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 28/50 JIT+ASM
  • 41. Loop unrolling int sum = 0; for (int i = 0; i < 1024; i++) sum += i; 30/50 JIT+ASM
  • 42. Loop unrolling int sum = 0; for (int i = 0; i < 1024; i++) sum += i; LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64 1.0 попугай 0.9 попугая 1.0 попугай 30/50 JIT+ASM
  • 43. Как же так? LegacyJIT-x64 xor ecx,ecx mov edx,1 Loop: lea eax,[rdx-1] add ecx,eax ; sum += (i) add ecx,edx ; sum += (i+1) lea eax,[rdx+1] add ecx,eax ; sum += (i+2) lea eax,[rdx+2] add ecx,eax ; sum += (i+3) add edx,4 ; i += 4 cmp edx,401h jl Loop 31/50 JIT+ASM
  • 45. Поговорим про параллелизм Но не будем разговаривать про потоки, задачи, PLINQ и прочий мультитрединг... 32/50 Parallelism
  • 46. Поговорим про 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; 33/50 Parallelism
  • 47. Поговорим про 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 попугай 33/50 Parallelism
  • 48. Как же так? ; 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 34/50 Parallelism
  • 49. Думаем про ASM double x = /* ... */ ; double a = x + 1; double b = x * 2; double c = Math.Sqrt(x); 35/50 Parallelism
  • 50. Думаем про ASM double x = /* ... */ ; double a = x + 1; double b = x * 2; double c = Math.Sqrt(x); LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64 (x87 FPU) (SSE2) (AVX) x + 1 faddp addsd vaddsd x * 2 fmul mulsd vmulsd Sqrt(X) fsqrt sqrtsd vsqrtsd 35/50 Parallelism
  • 52. Задачка 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); 37/50 Parallelism
  • 53. Задачка 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-x641 Sqrt13 40 попугаев Sqrt14 1 попугай 1 RyuJIT RC 37/50 Parallelism
  • 54. Как же так? 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 38/50 Parallelism
  • 55. Как же так? RyuJIT-x64, Sqrt14 vmovsd xmm0,qword ptr [7FF94F9C4C80h] ret 39/50 Parallelism
  • 56. Как же так? Большое дерево выражения * 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 // ... 40/50 Parallelism
  • 57. Как же так? 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]} ... 41/50 Parallelism
  • 59. Instruction-level parallelism public void Parallel() { a++; b++; c++; d++; } VS public void Sequential() { a++; a++; a++; a++; } 43/50 Parallelism
  • 60. Instruction-level parallelism public void Parallel() { a++; b++; c++; d++; } VS public void Sequential() { a++; a++; a++; a++; } LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64 Parallel 1.2 попугая 1 попугай 1.2 попугая Sequential 6.0 попугая 5.2 попугая 2.2 попугая 43/50 Parallelism
  • 61. Задачка public void Parallel() { x[0]++; x[1]++; x[2]++; x[3]++; } VS public void Sequential() { x[0]++; x[0]++; x[0]++; x[0]++; } 44/50 Parallelism
  • 62. Задачка public void Parallel() { x[0]++; x[1]++; x[2]++; x[3]++; } VS public void Sequential() { x[0]++; x[0]++; x[0]++; x[0]++; } LegacyJIT-x86 LegacyJIT-x64 RyuJIT-x64 Parallel 1.2 попугая 1 попугай 1 попугай Sequential 3.3 попугая 2.7 попугая 1 попугай 44/50 Parallelism
  • 63. Как же так? ; LegacyJIT-x64, Sequential ; x[0]++; mov rdx,qword ptr [rcx+8] mov rax,qword ptr [rdx+8] test rax,rax jbe IndexOutOfRangeException inc dword ptr [rdx+10h] ; x[0]++; mov rdx,qword ptr [rcx+8] mov rax,qword ptr [rdx+8] test rax,rax jbe IndexOutOfRangeException inc dword ptr [rdx+10h] ; ... ; RyuJIT-x64, Sequential ; x[0]++; mov rax,qword ptr [rcx+8] cmp dword ptr [rax+8],0 jbe IndexOutOfRangeException add rax,10h mov rdx,rax mov ecx,dword ptr [rdx] inc ecx mov dword ptr [rdx],ecx ; x[0]++; mov rdx,rax inc ecx mov dword ptr [rdx],ecx ; ... 45/50 Parallelism
  • 65. Задачка 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; } 47/50 Parallelism
  • 66. Задачка 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 47/50 Parallelism
  • 67. Как же так? ; 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 См. также: https://github.com/dotnet/coreclr/issues/993 48/50 Parallelism
  • 68. Методическая литература Для успешных микрооптимизаций нужно очень много знать: 49/50