Продолжаем говорить о микрооптимизациях
.NET-приложений
Андрей Акиньшин, JetBrains
.NEXT 2015 Moscow
1/32
Про что будем разговаривать?
2/32
Про что будем разговаривать?
Не будет:
• Универсальных способов оптимизации
• Скучной теории
• Подробного устройства .NET, GC, JIT, CPU
2/32
Про что будем разговаривать?
Не будет:
• Универсальных способов оптимизации
• Скучной теории
• Подробного устройства .NET, GC, JIT, CPU
Будет:
• Много весёлых историй1
1
Все истории основаны на реальных событиях
2/32
Roslyn
3/32 Roslyn
Roslyn
Задачка:
void Print(string format,
params object[] args)
{
// ...
}
Print("DotNext"); // Будет ли аллокация?
4/32 Roslyn
Roslyn
Задачка:
void Print(string format,
params object[] args)
{
// ...
}
Print("DotNext"); // Будет ли аллокация?
old csc.exe Roslyn
new object[0] System.Array.Empty<T>1
1
.NET Framework 4.6+
4/32 Roslyn
Boxing
5/32 Boxing
Boxing
Будет ли упаковка/распаковка?
[MethodImpl(MethodImplOptions.NoInlining)]
private static TBase Foo<T, TBase>(T t)
where T : TBase
{
return (TBase) t;
}
6/32 Boxing
Boxing
Будет ли упаковка/распаковка?
[MethodImpl(MethodImplOptions.NoInlining)]
private static TBase Foo<T, TBase>(T t)
where T : TBase
{
return (TBase) t;
}
Взглянем на IL:
IL_0000: ldarg.0 ; t
IL_0001: box 0 ; T
IL_0006: unbox.any 1 ; TBase
IL_000b: ret
6/32 Boxing
Примеры
struct Bar {}
struct Bar<T> {}
Foo<int, int>(1);
Foo<long, long>(2);
Foo<Bar, Bar>(new Bar());
Foo<Bar<int>, Bar<int>>(new Bar<int>());
Foo<Bar<IList>, Bar<IList>>(new Bar<IList>());
7/32 Boxing
Примеры
struct Bar {}
struct Bar<T> {}
Foo<int, int>(1);
Foo<long, long>(2);
Foo<Bar, Bar>(new Bar());
Foo<Bar<int>, Bar<int>>(new Bar<int>());
Foo<Bar<IList>, Bar<IList>>(new Bar<IList>());
Но не сможет ли JIT нам помочь?
7/32 Boxing
Иногда сможет
Foo<int, int>(1);
Foo<long, long>(2);
Foo<Bar, Bar>(new Bar());
Foo<Bar<int>, Bar<int>>(new Bar<int>());
⇓
mov eax,ecx ; (С точностью до регистра)
ret
8/32 Boxing
А иногда нет
Foo<Bar<IList>, Bar<IList>>(new Bar<IList>());
⇓
sub rsp,28h
mov dword ptr [rsp+30h],ecx
mov rax,qword ptr [rsp+30h]
mov rcx,qword ptr [rax+10h]
mov rcx,qword ptr [rcx]
btr rcx,0
cmovb rcx,qword ptr [rcx]
lea rdx,[rsp+38h]
call 00007FF8C37D48D0
mov rdx,rax
mov rax,qword ptr [rsp+30h]
mov rcx,qword ptr [rax+10h]
mov rcx,qword ptr [rcx+8]
btr rcx,0
cmovb rcx,qword ptr [rcx]
call 00007FF8C3896870
mov al,byte ptr [rax]
add rsp,28h
ret
9/32 Boxing
GC
10/32 GC
GC
class Foo
{
public object Bar { get; set; }
}
var foo = new Foo() { Bar = new object() };
// Long live the Bar!
GC.KeepAlive(foo);
11/32 GC
GC
class Foo
{
public object Bar { get; set; }
}
var foo = new Foo() { Bar = new object() };
// Long live the Bar!
GC.KeepAlive(foo);
Уважаемые знатоки, внимание, вопрос:
Может ли string удерживать ссылку на object?
11/32 GC
GC
class Foo
{
public object Bar { get; set; }
}
var foo = new Foo() { Bar = new object() };
// Long live the Bar!
GC.KeepAlive(foo);
Уважаемые знатоки, внимание, вопрос:
Может ли string удерживать ссылку на object?
var foo = "DotNext";
var bar = new object();
BlackBox(); // Что же находится в чёрном ящике?
// Long live the Bar!
GC.KeepAlive(foo);
11/32 GC
GC
Удивительный BCL:
ConditionalWeakTable<TKey, TValue> Class
Enables compilers to dynamically attach object fields to managed objects.
c MSDN
12/32 GC
GC
Удивительный BCL:
ConditionalWeakTable<TKey, TValue> Class
Enables compilers to dynamically attach object fields to managed objects.
c MSDN
А давайте немножко пошалим:
var foo = "DotNext";
var bar = new object();
var cwt = new ConditionalWeakTable<string, object>();
cwt.Add(foo, bar);
String.Intern(foo);
// foo держит ссылку на bar
GC.KeepAlive(cwt);
12/32 GC
OS
13/32 OS
OS
14/32 OS
Guid
15/32 OS
Benchmarks
16/32 Benchmarks
Benchmarks
Что там может быть сложного?
17/32 Benchmarks
Benchmarks
Что там может быть сложного?
// Нужно замерить время?
public double WithoutStopwatch()
{
double a = 1, b = 1;
for (int i = 0; i < 100; i++)
a = a + b;
return a;
}
17/32 Benchmarks
Benchmarks
Что там может быть сложного?
// Нужно замерить время?
public double WithoutStopwatch()
{
double a = 1, b = 1;
for (int i = 0; i < 100; i++)
a = a + b;
return a;
}
// У нас же есть Stopwatch!
public double WithStopwatch()
{
double a = 1, b = 1;
var sw = Stopwatch.Start();
for (int i = 0; i < 100; i++)
a = a + b;
Print(sw.ElapsedMilliseconds);
return a;
}
17/32 Benchmarks
Benchmarks
Что там может быть сложного?
// Нужно замерить время?
public double WithoutStopwatch()
{
double a = 1, b = 1;
for (int i = 0; i < 100; i++)
a = a + b;
return a;
}
// У нас же есть Stopwatch!
public double WithStopwatch()
{
double a = 1, b = 1;
var sw = Stopwatch.Start();
for (int i = 0; i < 100; i++)
a = a + b;
Print(sw.ElapsedMilliseconds);
return a;
}
WithoutStopwatch WithStopwatch
∼100ns ∼350ns
17/32 Benchmarks
Benchmarks
Что там может быть сложного?
// Нужно замерить время?
public double WithoutStopwatch()
{
double a = 1, b = 1;
for (int i = 0; i < 100; i++)
a = a + b;
return a;
}
// У нас же есть Stopwatch!
public double WithStopwatch()
{
double a = 1, b = 1;
var sw = Stopwatch.Start();
for (int i = 0; i < 100; i++)
a = a + b;
Print(sw.ElapsedMilliseconds);
return a;
}
WithoutStopwatch WithStopwatch
∼100ns ∼350ns
; a + b
fld1
faddp st(1),st
; a + b
fld1
fadd qword ptr [ebp-0Ch]
fstp qword ptr [ebp-0Ch]
17/32 Benchmarks
RyuJIT
18/32 RyuJIT
RyuJIT
Если бы вы были JIT-компилятором,
то как бы вы скомпилировали следующий код?
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong RotateRight64(ulong value)
{
return (value >> 1) | (value << 63);
}
19/32 RyuJIT
RyuJIT
Если бы вы были JIT-компилятором,
то как бы вы скомпилировали следующий код?
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong RotateRight64(ulong value)
{
return (value >> 1) | (value << 63);
}
Может быть так?
mov rdx,rax ; value
shr rdx,1 ; value >> 1
shl rax,3Fh ; value << 63
or eax,edx ; (value >> 1) | (value << 63)
19/32 RyuJIT
RyuJIT
Если бы вы были JIT-компилятором,
то как бы вы скомпилировали следующий код?
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong RotateRight64(ulong value)
{
return (value >> 1) | (value << 63);
}
Может быть так?
mov rdx,rax ; value
shr rdx,1 ; value >> 1
shl rax,3Fh ; value << 63
or eax,edx ; (value >> 1) | (value << 63)
Или так?
ror rax,1
19/32 RyuJIT
RyuJIT
Time Percent
ComputeHash 4047ms 100%
RotateRight 1487ms 37%
20/32 RyuJIT
RyuJIT
21/32 RyuJIT
Blittable
22/32 Blittable
Blittable
Загадка для любителей помаршалить:
[StructLayout(LayoutKind.Explicit)]
public struct UInt128
{
[FieldOffset(0)] public ulong Value1;
[FieldOffset(8)] public ulong Value2;
}
22/32 Blittable
Blittable
Загадка для любителей помаршалить:
[StructLayout(LayoutKind.Explicit)]
public struct UInt128
{
[FieldOffset(0)] public ulong Value1;
[FieldOffset(8)] public ulong Value2;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public UInt128 UInt128;
public char Char;
}
22/32 Blittable
Blittable
Загадка для любителей помаршалить:
[StructLayout(LayoutKind.Explicit)]
public struct UInt128
{
[FieldOffset(0)] public ulong Value1;
[FieldOffset(8)] public ulong Value2;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public UInt128 UInt128;
public char Char;
}
var myStruct = new MyStruct();
var baseAddress = (int)&myStruct;
var uInt128Address = (int)&myStruct.UInt128;
22/32 Blittable
Blittable
Загадка для любителей помаршалить:
[StructLayout(LayoutKind.Explicit)]
public struct UInt128
{
[FieldOffset(0)] public ulong Value1;
[FieldOffset(8)] public ulong Value2;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public UInt128 UInt128;
public char Char;
}
var myStruct = new MyStruct();
var baseAddress = (int)&myStruct;
var uInt128Address = (int)&myStruct.UInt128;
Console.WriteLine(uInt128Address - baseAddress); // ???
Console.WriteLine(Marshal.OffsetOf(typeof(MyStruct), "UInt128")); // ???
22/32 Blittable
Blittable
Загадка для любителей помаршалить:
[StructLayout(LayoutKind.Explicit)]
public struct UInt128
{
[FieldOffset(0)] public ulong Value1;
[FieldOffset(8)] public ulong Value2;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public UInt128 UInt128;
public char Char;
}
var myStruct = new MyStruct();
var baseAddress = (int)&myStruct;
var uInt128Address = (int)&myStruct.UInt128;
Console.WriteLine(uInt128Address - baseAddress); // ???
Console.WriteLine(Marshal.OffsetOf(typeof(MyStruct), "UInt128")); // ???
MS.NET-x86 MS.NET-x64 Mono
Address 4 8 0
Marshal 0 0 0
22/32 Blittable
Memory
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
c Systems Performance: Enterprise and the Cloud
23/32 Memory
Memory
Задача: подсчитать сумму элементов массива
const int N = 1024;
int[,] a = new int[N, N];
24/32 Memory
Memory
Задача: подсчитать сумму элементов массива
const int N = 1024;
int[,] a = new int[N, N];
[Benchmark]
public double Sum_ij()
{
var sum = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
sum += a[i, j];
return sum;
}
24/32 Memory
Memory
Задача: подсчитать сумму элементов массива
const int N = 1024;
int[,] a = new int[N, N];
[Benchmark]
public double Sum_ij()
{
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 Sum_ji()
{
var sum = 0;
for (int j = 0; j < N; j++)
for (int i = 0; i < N; i++)
sum += a[i, j];
return sum;
}
24/32 Memory
Memory
Задача: подсчитать сумму элементов массива
const int N = 1024;
int[,] a = new int[N, N];
[Benchmark]
public double Sum_ij()
{
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 Sum_ji()
{
var sum = 0;
for (int j = 0; j < N; j++)
for (int i = 0; i < N; i++)
sum += a[i, j];
return sum;
}
Sum_ij() Sum_ji()
∼1.5ms ∼9ms
∗
LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz
24/32 Memory
Memory
25/32 Memory
stackalloc
26/32 stackalloc
stackalloc
for (int i = 0; i < 10000000; i++)
{
// Alloc class on heap
var hello = new HelloClassOnStack(random);
result += hello.Compute(i);
}
vs
for (int i = 0; i < 10000000; i++)
{
// Alloc class on stack
var hello = stackalloc HelloClassOnStack(random);
result += hello.Compute(i);
}
27/32 stackalloc
stackalloc
for (int i = 0; i < 10000000; i++)
{
// Alloc class on heap
var hello = new HelloClassOnStack(random);
result += hello.Compute(i);
}
vs
for (int i = 0; i < 10000000; i++)
{
// Alloc class on stack
var hello = stackalloc HelloClassOnStack(random);
result += hello.Compute(i);
}
Time GC Collect
stack ∼400ms 0
heap ∼5000ms 100+
27/32 stackalloc
Branch prediction
28/32 Branch prediction
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;
}
29/32 Branch prediction
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);
}
29/32 Branch prediction
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
∼20µs ∼150µs
∗
LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz
29/32 Branch prediction
Branch prediction
Branchless version:
private static int Sum(int[] data)
{
int sum = 0;
for (int i = 0; i < N; i++)
{
// if (data[i] >= 128)
// sum += data[i];
int t = (data[i] - 128) >> 31;
sum += ~t & data[i];
}
return sum;
}
30/32 Branch prediction
Branch prediction
Branchless version:
private static int Sum(int[] data)
{
int sum = 0;
for (int i = 0; i < N; i++)
{
// if (data[i] >= 128)
// sum += data[i];
int t = (data[i] - 128) >> 31;
sum += ~t & data[i];
}
return sum;
}
Sorted Unsorted
Branch ∼20µs ∼150µs
Branchless ∼30µs ∼30µs
∗
LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz
30/32 Branch prediction
Методическая литература
31/32
Вопросы?
Андрей Акиньшин, JetBrains
http://aakinshin.net
https://github.com/AndreyAkinshin
https://twitter.com/andrey_akinshin
andrey.akinshin@gmail.com
32/32

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

  • 1.
    Продолжаем говорить омикрооптимизациях .NET-приложений Андрей Акиньшин, JetBrains .NEXT 2015 Moscow 1/32
  • 2.
    Про что будемразговаривать? 2/32
  • 3.
    Про что будемразговаривать? Не будет: • Универсальных способов оптимизации • Скучной теории • Подробного устройства .NET, GC, JIT, CPU 2/32
  • 4.
    Про что будемразговаривать? Не будет: • Универсальных способов оптимизации • Скучной теории • Подробного устройства .NET, GC, JIT, CPU Будет: • Много весёлых историй1 1 Все истории основаны на реальных событиях 2/32
  • 5.
  • 6.
    Roslyn Задачка: void Print(string format, paramsobject[] args) { // ... } Print("DotNext"); // Будет ли аллокация? 4/32 Roslyn
  • 7.
    Roslyn Задачка: void Print(string format, paramsobject[] args) { // ... } Print("DotNext"); // Будет ли аллокация? old csc.exe Roslyn new object[0] System.Array.Empty<T>1 1 .NET Framework 4.6+ 4/32 Roslyn
  • 8.
  • 9.
    Boxing Будет ли упаковка/распаковка? [MethodImpl(MethodImplOptions.NoInlining)] privatestatic TBase Foo<T, TBase>(T t) where T : TBase { return (TBase) t; } 6/32 Boxing
  • 10.
    Boxing Будет ли упаковка/распаковка? [MethodImpl(MethodImplOptions.NoInlining)] privatestatic TBase Foo<T, TBase>(T t) where T : TBase { return (TBase) t; } Взглянем на IL: IL_0000: ldarg.0 ; t IL_0001: box 0 ; T IL_0006: unbox.any 1 ; TBase IL_000b: ret 6/32 Boxing
  • 11.
    Примеры struct Bar {} structBar<T> {} Foo<int, int>(1); Foo<long, long>(2); Foo<Bar, Bar>(new Bar()); Foo<Bar<int>, Bar<int>>(new Bar<int>()); Foo<Bar<IList>, Bar<IList>>(new Bar<IList>()); 7/32 Boxing
  • 12.
    Примеры struct Bar {} structBar<T> {} Foo<int, int>(1); Foo<long, long>(2); Foo<Bar, Bar>(new Bar()); Foo<Bar<int>, Bar<int>>(new Bar<int>()); Foo<Bar<IList>, Bar<IList>>(new Bar<IList>()); Но не сможет ли JIT нам помочь? 7/32 Boxing
  • 13.
    Иногда сможет Foo<int, int>(1); Foo<long,long>(2); Foo<Bar, Bar>(new Bar()); Foo<Bar<int>, Bar<int>>(new Bar<int>()); ⇓ mov eax,ecx ; (С точностью до регистра) ret 8/32 Boxing
  • 14.
    А иногда нет Foo<Bar<IList>,Bar<IList>>(new Bar<IList>()); ⇓ sub rsp,28h mov dword ptr [rsp+30h],ecx mov rax,qword ptr [rsp+30h] mov rcx,qword ptr [rax+10h] mov rcx,qword ptr [rcx] btr rcx,0 cmovb rcx,qword ptr [rcx] lea rdx,[rsp+38h] call 00007FF8C37D48D0 mov rdx,rax mov rax,qword ptr [rsp+30h] mov rcx,qword ptr [rax+10h] mov rcx,qword ptr [rcx+8] btr rcx,0 cmovb rcx,qword ptr [rcx] call 00007FF8C3896870 mov al,byte ptr [rax] add rsp,28h ret 9/32 Boxing
  • 15.
  • 16.
    GC class Foo { public objectBar { get; set; } } var foo = new Foo() { Bar = new object() }; // Long live the Bar! GC.KeepAlive(foo); 11/32 GC
  • 17.
    GC class Foo { public objectBar { get; set; } } var foo = new Foo() { Bar = new object() }; // Long live the Bar! GC.KeepAlive(foo); Уважаемые знатоки, внимание, вопрос: Может ли string удерживать ссылку на object? 11/32 GC
  • 18.
    GC class Foo { public objectBar { get; set; } } var foo = new Foo() { Bar = new object() }; // Long live the Bar! GC.KeepAlive(foo); Уважаемые знатоки, внимание, вопрос: Может ли string удерживать ссылку на object? var foo = "DotNext"; var bar = new object(); BlackBox(); // Что же находится в чёрном ящике? // Long live the Bar! GC.KeepAlive(foo); 11/32 GC
  • 19.
    GC Удивительный BCL: ConditionalWeakTable<TKey, TValue>Class Enables compilers to dynamically attach object fields to managed objects. c MSDN 12/32 GC
  • 20.
    GC Удивительный BCL: ConditionalWeakTable<TKey, TValue>Class Enables compilers to dynamically attach object fields to managed objects. c MSDN А давайте немножко пошалим: var foo = "DotNext"; var bar = new object(); var cwt = new ConditionalWeakTable<string, object>(); cwt.Add(foo, bar); String.Intern(foo); // foo держит ссылку на bar GC.KeepAlive(cwt); 12/32 GC
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
    Benchmarks Что там можетбыть сложного? 17/32 Benchmarks
  • 26.
    Benchmarks Что там можетбыть сложного? // Нужно замерить время? public double WithoutStopwatch() { double a = 1, b = 1; for (int i = 0; i < 100; i++) a = a + b; return a; } 17/32 Benchmarks
  • 27.
    Benchmarks Что там можетбыть сложного? // Нужно замерить время? public double WithoutStopwatch() { double a = 1, b = 1; for (int i = 0; i < 100; i++) a = a + b; return a; } // У нас же есть Stopwatch! public double WithStopwatch() { double a = 1, b = 1; var sw = Stopwatch.Start(); for (int i = 0; i < 100; i++) a = a + b; Print(sw.ElapsedMilliseconds); return a; } 17/32 Benchmarks
  • 28.
    Benchmarks Что там можетбыть сложного? // Нужно замерить время? public double WithoutStopwatch() { double a = 1, b = 1; for (int i = 0; i < 100; i++) a = a + b; return a; } // У нас же есть Stopwatch! public double WithStopwatch() { double a = 1, b = 1; var sw = Stopwatch.Start(); for (int i = 0; i < 100; i++) a = a + b; Print(sw.ElapsedMilliseconds); return a; } WithoutStopwatch WithStopwatch ∼100ns ∼350ns 17/32 Benchmarks
  • 29.
    Benchmarks Что там можетбыть сложного? // Нужно замерить время? public double WithoutStopwatch() { double a = 1, b = 1; for (int i = 0; i < 100; i++) a = a + b; return a; } // У нас же есть Stopwatch! public double WithStopwatch() { double a = 1, b = 1; var sw = Stopwatch.Start(); for (int i = 0; i < 100; i++) a = a + b; Print(sw.ElapsedMilliseconds); return a; } WithoutStopwatch WithStopwatch ∼100ns ∼350ns ; a + b fld1 faddp st(1),st ; a + b fld1 fadd qword ptr [ebp-0Ch] fstp qword ptr [ebp-0Ch] 17/32 Benchmarks
  • 30.
  • 31.
    RyuJIT Если бы выбыли JIT-компилятором, то как бы вы скомпилировали следующий код? [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateRight64(ulong value) { return (value >> 1) | (value << 63); } 19/32 RyuJIT
  • 32.
    RyuJIT Если бы выбыли JIT-компилятором, то как бы вы скомпилировали следующий код? [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateRight64(ulong value) { return (value >> 1) | (value << 63); } Может быть так? mov rdx,rax ; value shr rdx,1 ; value >> 1 shl rax,3Fh ; value << 63 or eax,edx ; (value >> 1) | (value << 63) 19/32 RyuJIT
  • 33.
    RyuJIT Если бы выбыли JIT-компилятором, то как бы вы скомпилировали следующий код? [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateRight64(ulong value) { return (value >> 1) | (value << 63); } Может быть так? mov rdx,rax ; value shr rdx,1 ; value >> 1 shl rax,3Fh ; value << 63 or eax,edx ; (value >> 1) | (value << 63) Или так? ror rax,1 19/32 RyuJIT
  • 34.
    RyuJIT Time Percent ComputeHash 4047ms100% RotateRight 1487ms 37% 20/32 RyuJIT
  • 35.
  • 36.
  • 37.
    Blittable Загадка для любителейпомаршалить: [StructLayout(LayoutKind.Explicit)] public struct UInt128 { [FieldOffset(0)] public ulong Value1; [FieldOffset(8)] public ulong Value2; } 22/32 Blittable
  • 38.
    Blittable Загадка для любителейпомаршалить: [StructLayout(LayoutKind.Explicit)] public struct UInt128 { [FieldOffset(0)] public ulong Value1; [FieldOffset(8)] public ulong Value2; } [StructLayout(LayoutKind.Sequential)] public struct MyStruct { public UInt128 UInt128; public char Char; } 22/32 Blittable
  • 39.
    Blittable Загадка для любителейпомаршалить: [StructLayout(LayoutKind.Explicit)] public struct UInt128 { [FieldOffset(0)] public ulong Value1; [FieldOffset(8)] public ulong Value2; } [StructLayout(LayoutKind.Sequential)] public struct MyStruct { public UInt128 UInt128; public char Char; } var myStruct = new MyStruct(); var baseAddress = (int)&myStruct; var uInt128Address = (int)&myStruct.UInt128; 22/32 Blittable
  • 40.
    Blittable Загадка для любителейпомаршалить: [StructLayout(LayoutKind.Explicit)] public struct UInt128 { [FieldOffset(0)] public ulong Value1; [FieldOffset(8)] public ulong Value2; } [StructLayout(LayoutKind.Sequential)] public struct MyStruct { public UInt128 UInt128; public char Char; } var myStruct = new MyStruct(); var baseAddress = (int)&myStruct; var uInt128Address = (int)&myStruct.UInt128; Console.WriteLine(uInt128Address - baseAddress); // ??? Console.WriteLine(Marshal.OffsetOf(typeof(MyStruct), "UInt128")); // ??? 22/32 Blittable
  • 41.
    Blittable Загадка для любителейпомаршалить: [StructLayout(LayoutKind.Explicit)] public struct UInt128 { [FieldOffset(0)] public ulong Value1; [FieldOffset(8)] public ulong Value2; } [StructLayout(LayoutKind.Sequential)] public struct MyStruct { public UInt128 UInt128; public char Char; } var myStruct = new MyStruct(); var baseAddress = (int)&myStruct; var uInt128Address = (int)&myStruct.UInt128; Console.WriteLine(uInt128Address - baseAddress); // ??? Console.WriteLine(Marshal.OffsetOf(typeof(MyStruct), "UInt128")); // ??? MS.NET-x86 MS.NET-x64 Mono Address 4 8 0 Marshal 0 0 0 22/32 Blittable
  • 42.
    Memory Event Latency Scaled 1CPU 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 c Systems Performance: Enterprise and the Cloud 23/32 Memory
  • 43.
    Memory Задача: подсчитать суммуэлементов массива const int N = 1024; int[,] a = new int[N, N]; 24/32 Memory
  • 44.
    Memory Задача: подсчитать суммуэлементов массива const int N = 1024; int[,] a = new int[N, N]; [Benchmark] public double Sum_ij() { var sum = 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) sum += a[i, j]; return sum; } 24/32 Memory
  • 45.
    Memory Задача: подсчитать суммуэлементов массива const int N = 1024; int[,] a = new int[N, N]; [Benchmark] public double Sum_ij() { 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 Sum_ji() { var sum = 0; for (int j = 0; j < N; j++) for (int i = 0; i < N; i++) sum += a[i, j]; return sum; } 24/32 Memory
  • 46.
    Memory Задача: подсчитать суммуэлементов массива const int N = 1024; int[,] a = new int[N, N]; [Benchmark] public double Sum_ij() { 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 Sum_ji() { var sum = 0; for (int j = 0; j < N; j++) for (int i = 0; i < N; i++) sum += a[i, j]; return sum; } Sum_ij() Sum_ji() ∼1.5ms ∼9ms ∗ LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz 24/32 Memory
  • 47.
  • 48.
  • 49.
    stackalloc for (int i= 0; i < 10000000; i++) { // Alloc class on heap var hello = new HelloClassOnStack(random); result += hello.Compute(i); } vs for (int i = 0; i < 10000000; i++) { // Alloc class on stack var hello = stackalloc HelloClassOnStack(random); result += hello.Compute(i); } 27/32 stackalloc
  • 50.
    stackalloc for (int i= 0; i < 10000000; i++) { // Alloc class on heap var hello = new HelloClassOnStack(random); result += hello.Compute(i); } vs for (int i = 0; i < 10000000; i++) { // Alloc class on stack var hello = stackalloc HelloClassOnStack(random); result += hello.Compute(i); } Time GC Collect stack ∼400ms 0 heap ∼5000ms 100+ 27/32 stackalloc
  • 51.
  • 52.
    Branch prediction const intN = 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; } 29/32 Branch prediction
  • 53.
    Branch prediction const intN = 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); } 29/32 Branch prediction
  • 54.
    Branch prediction const intN = 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 ∼20µs ∼150µs ∗ LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz 29/32 Branch prediction
  • 55.
    Branch prediction Branchless version: privatestatic int Sum(int[] data) { int sum = 0; for (int i = 0; i < N; i++) { // if (data[i] >= 128) // sum += data[i]; int t = (data[i] - 128) >> 31; sum += ~t & data[i]; } return sum; } 30/32 Branch prediction
  • 56.
    Branch prediction Branchless version: privatestatic int Sum(int[] data) { int sum = 0; for (int i = 0; i < N; i++) { // if (data[i] >= 128) // sum += data[i]; int t = (data[i] - 128) >> 31; sum += ~t & data[i]; } return sum; } Sorted Unsorted Branch ∼20µs ∼150µs Branchless ∼30µs ∼30µs ∗ LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz 30/32 Branch prediction
  • 57.
  • 58.