Successfully reported this slideshow.

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

1

Share

1 of 58
1 of 58

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

1

Share

Download to read offline

Этот доклад продолжает тему моего выступления с прошлого DotNext про сложную науку о микрооптимизациях. Вас ждут новые увлекательные истории о том, что же происходит под капотом .NET-программ. Будем обсуждать различия разных C# и JIT компиляторов (Roslyn и RyuJIT в том числе), медитировать на IL и ASM листинги, а также разбираться с особенностями современных CPU.

Этот доклад продолжает тему моего выступления с прошлого DotNext про сложную науку о микрооптимизациях. Вас ждут новые увлекательные истории о том, что же происходит под капотом .NET-программ. Будем обсуждать различия разных C# и JIT компиляторов (Roslyn и RyuJIT в том числе), медитировать на IL и ASM листинги, а также разбираться с особенностями современных CPU.

More Related Content

Viewers also liked

Related Books

Free with a 14 day trial from Scribd

See all

Related Audiobooks

Free with a 14 day trial from Scribd

See all

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

  1. 1. Продолжаем говорить о микрооптимизациях .NET-приложений Андрей Акиньшин, JetBrains .NEXT 2015 Moscow 1/32
  2. 2. Про что будем разговаривать? 2/32
  3. 3. Про что будем разговаривать? Не будет: • Универсальных способов оптимизации • Скучной теории • Подробного устройства .NET, GC, JIT, CPU 2/32
  4. 4. Про что будем разговаривать? Не будет: • Универсальных способов оптимизации • Скучной теории • Подробного устройства .NET, GC, JIT, CPU Будет: • Много весёлых историй1 1 Все истории основаны на реальных событиях 2/32
  5. 5. Roslyn 3/32 Roslyn
  6. 6. Roslyn Задачка: void Print(string format, params object[] args) { // ... } Print("DotNext"); // Будет ли аллокация? 4/32 Roslyn
  7. 7. 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
  8. 8. Boxing 5/32 Boxing
  9. 9. Boxing Будет ли упаковка/распаковка? [MethodImpl(MethodImplOptions.NoInlining)] private static TBase Foo<T, TBase>(T t) where T : TBase { return (TBase) t; } 6/32 Boxing
  10. 10. 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
  11. 11. Примеры 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
  12. 12. Примеры 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
  13. 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. 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. 15. GC 10/32 GC
  16. 16. 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
  17. 17. 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
  18. 18. 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
  19. 19. GC Удивительный BCL: ConditionalWeakTable<TKey, TValue> Class Enables compilers to dynamically attach object fields to managed objects. c MSDN 12/32 GC
  20. 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. 21. OS 13/32 OS
  22. 22. OS 14/32 OS
  23. 23. Guid 15/32 OS
  24. 24. Benchmarks 16/32 Benchmarks
  25. 25. Benchmarks Что там может быть сложного? 17/32 Benchmarks
  26. 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. 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. 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. 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. 30. RyuJIT 18/32 RyuJIT
  31. 31. RyuJIT Если бы вы были JIT-компилятором, то как бы вы скомпилировали следующий код? [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateRight64(ulong value) { return (value >> 1) | (value << 63); } 19/32 RyuJIT
  32. 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. 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. 34. RyuJIT Time Percent ComputeHash 4047ms 100% RotateRight 1487ms 37% 20/32 RyuJIT
  35. 35. RyuJIT 21/32 RyuJIT
  36. 36. Blittable 22/32 Blittable
  37. 37. Blittable Загадка для любителей помаршалить: [StructLayout(LayoutKind.Explicit)] public struct UInt128 { [FieldOffset(0)] public ulong Value1; [FieldOffset(8)] public ulong Value2; } 22/32 Blittable
  38. 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. 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. 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. 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. 42. 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
  43. 43. Memory Задача: подсчитать сумму элементов массива const int N = 1024; int[,] a = new int[N, N]; 24/32 Memory
  44. 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. 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. 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. 47. Memory 25/32 Memory
  48. 48. stackalloc 26/32 stackalloc
  49. 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. 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. 51. Branch prediction 28/32 Branch prediction
  52. 52. 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
  53. 53. 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
  54. 54. 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
  55. 55. 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
  56. 56. 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
  57. 57. Методическая литература 31/32
  58. 58. Вопросы? Андрей Акиньшин, JetBrains http://aakinshin.net https://github.com/AndreyAkinshin https://twitter.com/andrey_akinshin andrey.akinshin@gmail.com 32/32

×