Advertisement
Advertisement

More Related Content

Advertisement

More from Andrey Akinshin(20)

Advertisement

Низкоуровневые оптимизации .NET-приложений

  1. Íèçêîóðîâíåâûå îïòèìèçàöèè .NET-ïðèëîæåíèé Àíäðåé Àêèíüøèí Áàðíàóëüñêîå ñîîáùåñòâî .NET ðàçðàáîò÷èêîâ bug.ineta.ru www.facebook.com/groups/dotnetbarnaul/
  2. Îïòèìèçèðîâàòü èëè íå îïòèìèçèðîâàòü? Premature optimization is the root of all evil. c Donald Ervin Knuth
  3. Ïðàâèëüíûé ïîäõîä Õîðîøèå àëãîðèòìû Õîðîøèå ñòðóêòóðû äàííûõ Ìàëîå êîëè÷åñòâî òÿæ¼ëûõ îïåðàöèé
  4. Îïòèìèçàöèè
  5. Memory traffic Memory traffic ýòî ïëîõî. Ìíîãî îáúåêòîâ ! GC òÿæåëî. Ðàçíàÿ öåíà îáúåêòîâ â Gen0, Gen1, Gen2, LOH. Ëèøíèé memory traffic: LINQ, óïàêîâêà, áîëüøèå îáúåêòû, îòñóòñòâèå èíòåðíèðîâàíèÿ è ïóëîâ îáúåêòîâ è ìíîãîå äðóãîå.
  6. Memory traffic: çàìûêàíèÿ void Foo(Funcint, int inc) { } void Run() { int y = 1; Foo(x = x + y); }
  7. Memory traffic: çàìûêàíèÿ private void Foo(Funcint, int inc) {} private void Run() { Program.c__DisplayClass1 cDisplayClass1 = new Program.c__DisplayClass1(); cDisplayClass1.y = 1; this.Foo(new Funcint, int((object) cDisplayClass1, __methodptr(b__0))); } [CompilerGenerated] private sealed class c__DisplayClass1 { public int y; public c__DisplayClass1() { base..ctor(); } public int Runb__0(int x) { return x + this.y; } }
  8. Memory traffic: çàìûêàíèÿ void Foo(Funcobject before, Funcobject after) { before(); // Some logic after(); } void Run() { var a = new object(); var b = new object(); Foo(() = a, () = b); }
  9. Memory traffic: çàìûêàíèÿ private void Run() { Program.c__DisplayClass2 cDisplayClass2 = new Program.c__DisplayClass2(); cDisplayClass2.a = new object(); cDisplayClass2.b = new object(); this.Foo(new Funcobject((object) cDisplayClass2, __methodptr(Runb__0)), new Funcobject((object) cDisplayClass2, __methodptr(Runb__1))); } [CompilerGenerated] private sealed class c__DisplayClass2 { public object a; public object b; public c__DisplayClass2() { base..ctor(); } public object Runb__0() { return this.a; } public object Runb__1() { return this.b; } }
  10. Memory traffic: çàìûêàíèÿ void Foo(Funcint, int inc) { } static int y = 1; static int StaticInc(int x) { return x + y; } void Run() { Foo(x = StaticInc(x)); Foo(StaticInc); }
  11. Memory traffic: çàìûêàíèÿ private void Run() { if (Program.CS$9__CachedAnonymousMethodDelegate1 == null) { Program.CS$9__CachedAnonymousMethodDelegate1 = new Funcint, int((object) null, __methodptr(Runb__0)); } this.Foo(Program.CS$9__CachedAnonymousMethodDelegate1); this.Foo(new Funcint, int((object) null, __methodptr(StaticInc))); } [CompilerGenerated] private static int Runb__0(int x) { return Program.StaticInc(x); } [CompilerGenerated] private static Funcint, int CS$9__CachedAnonymousMethodDelegate1;
  12. Memory traffic: params void Foo(params int[] x) { } void Main() { Foo(); // IL_0001: ldarg.0 // IL_0002: ldc.i4.0 // IL_0003: newarr // IL_0008: call }
  13. Memory traffic: yeild IEnumerableint Foo() { for (int i = 0; i 5; i++) yield return i; }
  14. Memory traffic: yield private IEnumerableint Foo() { Program.Food__0 fooD0 = new Program.Food__0(-2); fooD0.4__this = this; return (IEnumerableint) fooD0; } [CompilerGenerated] private sealed class Food__0 : IEnumerableint, IEnumerable, CIEnumeratorint, IEnumerator, IDisposable { private int 2__current; private int 1__state; private int l__initialThreadId; public int i5__1; public Program 4__this; ...
  15. Memory traffic: List vs IList void Foo1(Listint list) { var start = GC.GetTotalMemory(true); foreach (var i in list) Console.WriteLine(GC.GetTotalMemory(true) - start); } void Foo2(IListint list) { var start = GC.GetTotalMemory(true); foreach (var i in list) Console.WriteLine(GC.GetTotalMemory(true) - start); } void Run() { var list = new Listint { 1 }; Foo1(list); Foo2(list); }
  16. Memory traffic: ìàëåíüêèé List struct SmallListT : IListT { private T item1; private T item2; private T item3; private ListT otherItems; public ListT ToList() { ... } }
  17. Ìèêðîáåí÷ìàðêè Òðåáîâàíèÿ: Ïîëó÷åíèå ìåòðèê Âîñïðîèçâîäèìîñòü Îáúåêòèâíîñòü Îòñóòñòâèå ñàéä-ýôôåêòîâ
  18. Êàê çàïóñêàòü áåí÷ìàðê? Release mode Without debugging Ïðîãðåâ Çàïóñê íà îäíîì ÿäðå ×èñòîå îêðóæåíèå Ìíîãîêðàòíûé çàïóñê â ðàçíûõ îêðóæåíèÿõ
  19. Íóæíî ïîìíèòü ïðî: Dead code elmintation Inlining Folding Branch prediction 600+ äðóãèõ îïòèìèçàöèé
  20. Interface implementation interface IFoo { int Inc(int x); } class FastFoo : IFoo { public int Inc(int x) { return x + 1; } } class SlowFoo : IFoo { public int Inc(int x) { return 1 + x; } } void DoIt(IFoo foo) { for (int i = 0; i 1000000000; i++) foo.Inc(0); } DoIt(new FastFoo()); DoIt(new SlowFoo());
  21. 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 { get { return bits0; } } public long Bits1 { get { return bits1; } } public long Bits2 { get { return bits2; } } public long Bits3 { get { return bits3; } } } class Test { private readonly Int256 value; // private Int256 value; public Test() { value = new Int256(1L, 5L, 10L, 100L); } public long TotalValue { get { return value.Bits0 + value.Bits1 + value.Bits2 + value.Bits3; } } public void RunTest() { var sample = TotalValue; Stopwatch sw = Stopwatch.StartNew(); long total = 0; for (int i = 0; i 1000000000; i++) total += TotalValue; sw.Stop(); Console.WriteLine(Total time: {0}ms, sw.ElapsedMilliseconds); } static void Main() { new Test().RunTest(); } } c Jon Skeet, Micro-optimization: the surprising inefficiency of readonly fields
  22. Îñòîðîæíåå ñ ÎÎÏ! Êðàñèâûé äèçàéí íå âñåãäà ñî÷åòàåòñÿ ñ âûñîêîé ïðîèçâîäèòåëüíîñòüþ.
  23. Reflection static MemoryLeakFixer() { var fields = typeof(DisposableObject).GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); fieldInfo = fields.FirstOrDefault(f = f.FieldType == typeof(GCHandle)); } public static void FixAfterRelease(DisposableObject obj) { if (obj.IsDisposed) { var dataHandle = (GCHandle)fieldInfo.GetValue(obj); if (dataHandle.IsAllocated) dataHandle.Free(); } }
  24. StructLayout [StructLayout(LayoutKind.Explicit)] struct MyStruct { [FieldOffset(0)] public Int16 Value; [FieldOffset(0)] public Byte LowByte; } var s = new MyStruct(); s.Value = 256 + 100; Console.WriteLine(s.LowByte); // 100
  25. ×¼ðíàÿ ìàãèÿ public class MyObject { public long X; } public class Pumpkin { public int Y1; public int Y2; } public unsafe IntPtr GetAddress(object obj) { var typedReference = __makeref(obj); return *(IntPtr*)(typedReference); } public unsafe T ConvertT(IntPtr address) { var fakeInstance = default(T); var typedReference = __makeref(fakeInstance); *(IntPtr*)(typedReference) = address; return __refvalue( typedReference,T); } public void Run() { var myObject = new MyObject { X = 1 + (2L 32) }; var pumpkin = ConvertPumpkin(GetAddress(myObject)); Console.WriteLine(pumpkin.Y1 + + pumpkin.Y2); // 1 2 myObject.X = 3 + (4L 32); Console.WriteLine(pumpkin.Y1 + + pumpkin.Y2); // 3 4 }
  26. Íóæíî ïîíèìàòü // sscli20clrsrcvmtypehandle.h // A TypeHandle is the FUNDAMENTAL concept of type identity in the CLR. // That is two types are equal if and only if their type handles // are equal. A TypeHandle, is a pointer sized struture that encodes // everything you need to know to figure out what kind of type you are // actually dealing with. // At the present time a TypeHandle can point at two possible things // // 1) A MethodTable (Intrinsics, Classes, Value Types and their instantiations) // 2) A TypeDesc (all other cases: arrays, byrefs, pointer types, // function pointers, generic type variables) // // or with IL stubs, a third thing: // // 3) A MethodTable for a native value type.
  27. Íóæíî ïîíèìàòü private void Print(Type type) { bool isTypeDesc = ((int)type.TypeHandle.Value 2) 0; Console.WriteLine({0}: {1} = {2}, type.Name.PadRight(10), type.TypeHandle.Value.ToString(X), (isTypeDesc ? TypeDesc : MethodTable)); } private void Run() { Print(typeof(int)); Print(typeof(object)); Print(typeof(Stream)); Print(typeof(int[])); Print(typeof(int[][])); Print(typeof(object[])); } // Int32 : 65C4C480 = MethodTable // Object : 65C4B060 = MethodTable // Stream : 65C4D954 = MethodTable // Int32[] : 65854C8A = TypeDesc // Int32[][] : 658F6BD6 = TypeDesc // Object[] : 65854D7A = TypeDesc
  28. Öèêëû public int Foo997() { int sum = 0; for (int i = 0; i 997; i++) sum += a[i]; return sum; } public int Foo1000() { int sum = 0; for (int i = 0; i 1000; i++) sum += a[i]; return sum; }
  29. Öèêëû
  30. Ïàðàëëåëèçì èíñòðóêöèé int iterationCount = 256 * 1024 * 1024; int[] a = new int[2]; for (int i = 0; i iterationCount; i++) { a[0]++; a[0]++; } for (int i = 0; i iterationCount; i++) { a[0]++; a[1]++; }
  31. Êýø ïðîöåññîðà int[] x = new int[64 * 1024 * 1024]; for (int i = 0; i x.Length; i++) x[i] *= 3; for (int i = 0; i x.Length; i += 16) x[i] *= 3;
  32. Êýø ïðîöåññîðà Óìíîæåíèå ìàòðèö: // Standard for (k = 0; k n; k++) for (i = 0; i n; i++) for (j = 0; j n; j++) c[k][i] = c[k][i] + a[k][j]*b[j][i]; // Optimized for (k = 0; k n; k++) for (i = 0; i n; i++) for (j = 0; j n; j++) c[i][j] = c[i][j] + a[i][k]*b[k][j];
  33. Êýø ïðîöåññîðà
  34. Êýø ïðîöåññîðà Binary Search
  35. Êýø ïðîöåññîðà Cache-Conscious Binary Search
  36. Êýø ïðîöåññîðà False sharing private const int Step = 1; private static int[] x = new int[1024]; private void Foo(int p) { for (int j = 0; j 1000000000; j++) x[p] = x[p] + 3; } private void Run() { var s = Stopwatch.StartNew(); var tasks = new Task[4]; tasks[0] = Task.Factory.StartNew(() = Foo(0 * Step)); tasks[1] = Task.Factory.StartNew(() = Foo(1 * Step)); tasks[2] = Task.Factory.StartNew(() = Foo(2 * Step)); tasks[3] = Task.Factory.StartNew(() = Foo(3 * Step)); Task.WaitAll(tasks); Console.WriteLine(s.ElapsedMilliseconds); } À òåïåðü ïîäóìàéòå ïðî CardTable...
  37. Íàïóòñòâèå Îïòèìèçèðóéòå îñòîðîæíî!
Advertisement