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-ïðèëîæåíèé 
Àíäðåé Àêèíüøèí 
Áàðíàóëüñêîå ñîîáùåñòâî .NET ðàçðàáîò÷èêîâ 
bug.ineta.ru 
www...
Îïòèìèçèðîâàòü èëè íå 
îïòèìèçèðîâàòü? 
Premature optimization is the root of all evil. 

c Donald Ervin Knuth
Ïðàâèëüíûé ïîäõîä 
 Õîðîøèå àëãîðèòìû 
 Õîðîøèå ñòðóêòóðû äàííûõ 
 Ìàëîå êîëè÷åñòâî òÿæ¼ëûõ îïåðàöèé
Îïòèìèçàöèè
Memory traffic 
 Memory traffic  ýòî ïëîõî. 
 Ìíîãî îáúåêòîâ ! GC òÿæåëî. 
 Ðàçíàÿ öåíà îáúåêòîâ â Gen0, Gen1, Gen2, LOH. ...
Memory traffic: çàìûêàíèÿ 
void Foo(Funcint, int inc) { } 
void Run() 
{ 
int y = 1; 
Foo(x = x + y); 
}
Memory traffic: çàìûêàíèÿ 
private void Foo(Funcint, int inc) 
{} 
private void Run() 
{ 
Program.c__DisplayClass1 cDispla...
Memory traffic: çàìûêàíèÿ 
void Foo(Funcobject before, 
Funcobject after) 
{ 
before(); 
// Some logic 
after(); 
} 
void ...
Memory traffic: çàìûêàíèÿ 
private void Run() 
{ 
Program.c__DisplayClass2 cDisplayClass2 = new Program.c__DisplayClass2()...
Memory traffic: çàìûêàíèÿ 
void Foo(Funcint, int inc) { } 
static int y = 1; 
static int StaticInc(int x) { return x + y; ...
Memory traffic: çàìûêàíèÿ 
private void Run() 
{ 
if (Program.CS$9__CachedAnonymousMethodDelegate1 == null) 
{ 
Program.CS...
Memory traffic: params 
void Foo(params int[] x) { } 
void Main() 
{ 
Foo(); 
// IL_0001: ldarg.0 
// IL_0002: ldc.i4.0 
/...
Memory traffic: yeild 
IEnumerableint Foo() 
{ 
for (int i = 0; i  5; i++) 
yield return i; 
}
Memory traffic: yield 
private IEnumerableint Foo() 
{ 
Program.Food__0 fooD0 = new Program.Food__0(-2); 
fooD0.4__this = ...
Memory traffic: List vs IList 
void Foo1(Listint list) 
{ 
var start = GC.GetTotalMemory(true); 
foreach (var i in list) 
...
Memory traffic: ìàëåíüêèé List 
struct SmallListT : IListT 
{ 
private T item1; 
private T item2; 
private T item3; 
priva...
Ìèêðîáåí÷ìàðêè 
Òðåáîâàíèÿ: 
 Ïîëó÷åíèå ìåòðèê 
 Âîñïðîèçâîäèìîñòü 
 Îáúåêòèâíîñòü 
 Îòñóòñòâèå ñàéä-ýôôåêòîâ
Êàê çàïóñêàòü áåí÷ìàðê? 
 Release mode 
 Without debugging 
 Ïðîãðåâ 
 Çàïóñê íà îäíîì ÿäðå 
 ×èñòîå îêðóæåíèå 
 Ìíîãîêðàò...
Íóæíî ïîìíèòü ïðî: 
 Dead code elmintation 
 Inlining 
 Folding 
 Branch prediction 
 600+ äðóãèõ îïòèìèçàöèé
Interface implementation 
interface IFoo 
{ 
int Inc(int x); 
} 
class FastFoo : IFoo 
{ 
public int Inc(int x) 
{ 
return...
Readonly fields 
public struct Int256 
{ 
private readonly long bits0, bits1, bits2, bits3; 
public Int256(long bits0, lon...
Îñòîðîæíåå ñ ÎÎÏ! 
Êðàñèâûé äèçàéí íå âñåãäà ñî÷åòàåòñÿ ñ âûñîêîé 
ïðîèçâîäèòåëüíîñòüþ.
Reflection 
static MemoryLeakFixer() 
{ 
var fields = typeof(DisposableObject).GetFields( 
BindingFlags.Instance | Binding...
StructLayout 
[StructLayout(LayoutKind.Explicit)] 
struct MyStruct 
{ 
[FieldOffset(0)] 
public Int16 Value; 
[FieldOffset...
×¼ðíàÿ ìàãèÿ 
public class MyObject 
{ 
public long X; 
} 
public class Pumpkin 
{ 
public int Y1; 
public int Y2; 
} 
pub...
Íóæíî ïîíèìàòü 
// sscli20clrsrcvmtypehandle.h 
// A TypeHandle is the FUNDAMENTAL concept of type identity in the CLR. 
/...
Íóæíî ïîíèìàòü 
private void Print(Type type) 
{ 
bool isTypeDesc = ((int)type.TypeHandle.Value  2)  0; 
Console.WriteLine...
Öèêëû 
public int Foo997() 
{ 
int sum = 0; 
for (int i = 0; i  997; i++) 
sum += a[i]; 
return sum; 
} 
public int Foo100...
Öèêëû
Ïàðàëëåëèçì èíñòðóêöèé 
int iterationCount = 256 * 1024 * 1024; 
int[] a = new int[2]; 
for (int i = 0; i  iterationCount;...
Êýø ïðîöåññîðà 
int[] x = new int[64 * 1024 * 1024]; 
for (int i = 0; i  x.Length; i++) 
x[i] *= 3; 
for (int i = 0; i  x....
Êýø ïðîöåññîðà 
Óìíîæåíèå ìàòðèö: 
// Standard 
for (k = 0; k  n; k++) 
for (i = 0; i  n; i++) 
for (j = 0; j  n; j++) 
c[...
Êýø ïðîöåññîðà
Êýø ïðîöåññîðà 
Binary Search
Êýø ïðîöåññîðà 
Cache-Conscious Binary Search
Êýø ïðîöåññîðà 
False sharing 
private const int Step = 1; 
private static int[] x = new int[1024]; 
private void Foo(int ...
Íàïóòñòâèå 
Îïòèìèçèðóéòå îñòîðîæíî!
Upcoming SlideShare
Loading in …5
×

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

3,378 views

Published on

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

Published in: Education
  • Be the first to comment

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

  1. 1. Íèçêîóðîâíåâûå îïòèìèçàöèè .NET-ïðèëîæåíèé Àíäðåé Àêèíüøèí Áàðíàóëüñêîå ñîîáùåñòâî .NET ðàçðàáîò÷èêîâ bug.ineta.ru www.facebook.com/groups/dotnetbarnaul/
  2. 2. Îïòèìèçèðîâàòü èëè íå îïòèìèçèðîâàòü? Premature optimization is the root of all evil. c Donald Ervin Knuth
  3. 3. Ïðàâèëüíûé ïîäõîä Õîðîøèå àëãîðèòìû Õîðîøèå ñòðóêòóðû äàííûõ Ìàëîå êîëè÷åñòâî òÿæ¼ëûõ îïåðàöèé
  4. 4. Îïòèìèçàöèè
  5. 5. Memory traffic Memory traffic ýòî ïëîõî. Ìíîãî îáúåêòîâ ! GC òÿæåëî. Ðàçíàÿ öåíà îáúåêòîâ â Gen0, Gen1, Gen2, LOH. Ëèøíèé memory traffic: LINQ, óïàêîâêà, áîëüøèå îáúåêòû, îòñóòñòâèå èíòåðíèðîâàíèÿ è ïóëîâ îáúåêòîâ è ìíîãîå äðóãîå.
  6. 6. Memory traffic: çàìûêàíèÿ void Foo(Funcint, int inc) { } void Run() { int y = 1; Foo(x = x + y); }
  7. 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. 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. 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. 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. 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. 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. 13. Memory traffic: yeild IEnumerableint Foo() { for (int i = 0; i 5; i++) yield return i; }
  14. 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. 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. 16. Memory traffic: ìàëåíüêèé List struct SmallListT : IListT { private T item1; private T item2; private T item3; private ListT otherItems; public ListT ToList() { ... } }
  17. 17. Ìèêðîáåí÷ìàðêè Òðåáîâàíèÿ: Ïîëó÷åíèå ìåòðèê Âîñïðîèçâîäèìîñòü Îáúåêòèâíîñòü Îòñóòñòâèå ñàéä-ýôôåêòîâ
  18. 18. Êàê çàïóñêàòü áåí÷ìàðê? Release mode Without debugging Ïðîãðåâ Çàïóñê íà îäíîì ÿäðå ×èñòîå îêðóæåíèå Ìíîãîêðàòíûé çàïóñê â ðàçíûõ îêðóæåíèÿõ
  19. 19. Íóæíî ïîìíèòü ïðî: Dead code elmintation Inlining Folding Branch prediction 600+ äðóãèõ îïòèìèçàöèé
  20. 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. 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. 22. Îñòîðîæíåå ñ ÎÎÏ! Êðàñèâûé äèçàéí íå âñåãäà ñî÷åòàåòñÿ ñ âûñîêîé ïðîèçâîäèòåëüíîñòüþ.
  23. 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. 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. 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. 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. 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. 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. 29. Öèêëû
  30. 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. 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. 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. 33. Êýø ïðîöåññîðà
  34. 34. Êýø ïðîöåññîðà Binary Search
  35. 35. Êýø ïðîöåññîðà Cache-Conscious Binary Search
  36. 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. 37. Íàïóòñòâèå Îïòèìèçèðóéòå îñòîðîæíî!

×