Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

479 views

Published on

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

Published in: Software
  • Be the first to comment

Продолжаем говорить о микрооптимизациях .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

×