Upload
andrey-akinshin
View
392
Download
3
Embed Size (px)
Citation preview
Продолжаем говорить о микрооптимизациях.NET-приложений
Андрей Акиньшин, JetBrains
.NEXT 2015 Moscow
1/32
Про что будем разговаривать?
Не будет:• Универсальных способов оптимизации• Скучной теории• Подробного устройства .NET, GC, JIT, CPU
Будет:• Много весёлых историй1
2/32
Про что будем разговаривать?
Не будет:• Универсальных способов оптимизации• Скучной теории• Подробного устройства .NET, GC, JIT, CPU
Будет:• Много весёлых историй1
2/32
Про что будем разговаривать?
Не будет:• Универсальных способов оптимизации• Скучной теории• Подробного устройства .NET, GC, JIT, CPU
Будет:• Много весёлых историй1
1Все истории основаны на реальных событиях2/32
Roslyn
3/32 Roslyn
Roslyn
Задачка:
void Print(string format,params object[] args)
{// ...
}
Print("DotNext"); // Будет ли аллокация?
old csc.exe Roslynnew object[0] System.Array.Empty<T>1
4/32 Roslyn
Roslyn
Задачка:
void Print(string format,params object[] args)
{// ...
}
Print("DotNext"); // Будет ли аллокация?
old csc.exe Roslynnew 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;}
Взглянем на IL:IL_0000: ldarg.0 ; tIL_0001: box 0 ; TIL_0006: unbox.any 1 ; TBaseIL_000b: ret
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 ; tIL_0001: box 0 ; TIL_0006: unbox.any 1 ; TBaseIL_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>());
Но не сможет ли JIT нам помочь?
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,28hmov dword ptr [rsp+30h],ecxmov rax,qword ptr [rsp+30h]mov rcx,qword ptr [rax+10h]mov rcx,qword ptr [rcx]btr rcx,0cmovb rcx,qword ptr [rcx]lea rdx,[rsp+38h]call 00007FF8C37D48D0mov rdx,raxmov rax,qword ptr [rsp+30h]mov rcx,qword ptr [rax+10h]mov rcx,qword ptr [rcx+8]btr rcx,0cmovb rcx,qword ptr [rcx]call 00007FF8C3896870mov al,byte ptr [rax]add rsp,28hret
9/32 Boxing
GC
10/32 GC
GCclass 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
GCclass 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
GCclass 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
А давайте немножко пошалим:
var foo = "DotNext";var bar = new object();var cwt = new ConditionalWeakTable<string, object>();cwt.Add(foo, bar);String.Intern(foo);// foo держит ссылку на barGC.KeepAlive(cwt);
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 держит ссылку на barGC.KeepAlive(cwt);
12/32 GC
OS
13/32 OS
OS
14/32 OS
Guid
15/32 OS
Benchmarks
16/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 + bfld1faddp st(1),st
; a + bfld1fadd qword ptr [ebp-0Ch]fstp qword ptr [ebp-0Ch]
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 + bfld1faddp st(1),st
; a + bfld1fadd qword ptr [ebp-0Ch]fstp qword ptr [ebp-0Ch]
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 + bfld1faddp st(1),st
; a + bfld1fadd qword ptr [ebp-0Ch]fstp qword ptr [ebp-0Ch]
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 + bfld1faddp st(1),st
; a + bfld1fadd qword ptr [ebp-0Ch]fstp qword ptr [ebp-0Ch]
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 + bfld1faddp st(1),st
; a + bfld1fadd 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);}
Может быть так?mov rdx,rax ; valueshr rdx,1 ; value >> 1shl rax,3Fh ; value << 63or eax,edx ; (value >> 1) | (value << 63)
Или так?ror rax,1
19/32 RyuJIT
RyuJITЕсли бы вы были JIT-компилятором,
то как бы вы скомпилировали следующий код?[MethodImpl(MethodImplOptions.AggressiveInlining)]public static ulong RotateRight64(ulong value){
return (value >> 1) | (value << 63);}
Может быть так?mov rdx,rax ; valueshr rdx,1 ; value >> 1shl rax,3Fh ; value << 63or eax,edx ; (value >> 1) | (value << 63)
Или так?ror rax,1
19/32 RyuJIT
RyuJITЕсли бы вы были JIT-компилятором,
то как бы вы скомпилировали следующий код?[MethodImpl(MethodImplOptions.AggressiveInlining)]public static ulong RotateRight64(ulong value){
return (value >> 1) | (value << 63);}
Может быть так?mov rdx,rax ; valueshr rdx,1 ; value >> 1shl rax,3Fh ; value << 63or eax,edx ; (value >> 1) | (value << 63)
Или так?ror rax,1
19/32 RyuJIT
RyuJIT
Time PercentComputeHash 4047ms 100%RotateRight 1487ms 37%
20/32 RyuJIT
RyuJIT
21/32 RyuJIT
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 MonoAddress 4 8 0Marshal 0 0 0
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 MonoAddress 4 8 0Marshal 0 0 0
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 MonoAddress 4 8 0Marshal 0 0 0
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 MonoAddress 4 8 0Marshal 0 0 0
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 MonoAddress 4 8 0Marshal 0 0 0
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 MonoAddress 4 8 0Marshal 0 0 0
22/32 Blittable
Memory
Event Latency Scaled1 CPU cycle 0.3 ns 1 sLevel 1 cache access 0.9 ns 3 sLevel 2 cache access 2.8 ns 9 sLevel 3 cache access 12.9 ns 43 sMain memory access 120 ns 6 minSolid-state disk I/O 50-150 µs 2-6 daysRotational disk I/O 1-10 ms 1-12 monthsInternet: SF to NYC 40 ms 4 yearsInternet: SF to UK 81 ms 8 yearsInternet: SF to Australia 183 ms 19 yearsOS virtualization reboot 4 s 423 yearsSCSI command time-out 30 s 3000 yearsHardware virtualization reboot 40 s 4000 yearsPhysical system reboot 5 m 32 millenia
c© Systems Performance: Enterprise and the Cloud23/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
Задача: подсчитать сумму элементов массива
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
Задача: подсчитать сумму элементов массива
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
Задача: подсчитать сумму элементов массива
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.20GHz24/32 Memory
Memory
25/32 Memory
stackalloc
26/32 stackalloc
stackallocfor (int i = 0; i < 10000000; i++){
// Alloc class on heapvar hello = new HelloClassOnStack(random);result += hello.Compute(i);
}vs
for (int i = 0; i < 10000000; i++){
// Alloc class on stackvar hello = stackalloc HelloClassOnStack(random);result += hello.Compute(i);
}
Time GC Collectstack ∼400ms 0heap ∼5000ms 100+
27/32 stackalloc
stackallocfor (int i = 0; i < 10000000; i++){
// Alloc class on heapvar hello = new HelloClassOnStack(random);result += hello.Compute(i);
}vs
for (int i = 0; i < 10000000; i++){
// Alloc class on stackvar hello = stackalloc HelloClassOnStack(random);result += hello.Compute(i);
}
Time GC Collectstack ∼400ms 0heap ∼5000ms 100+
27/32 stackalloc
Branch prediction
28/32 Branch prediction
Branch predictionconst 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 predictionconst 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 predictionconst 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.20GHz29/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 UnsortedBranch ∼20µs ∼150µsBranchless ∼30µs ∼30µs
∗LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz
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 UnsortedBranch ∼20µs ∼150µsBranchless ∼30µs ∼30µs
∗LegacyJIT-x86, i7-4702MQ CPU @ 2.20GHz30/32 Branch prediction
Методическая литература
31/32
Вопросы?
Андрей Акиньшин, JetBrainshttp://aakinshin.net
https://github.com/AndreyAkinshinhttps://twitter.com/andrey_akinshin
32/32