58
Продолжаем говорить о микрооптимизациях .NET-приложений Андрей Акиньшин, JetBrains .NEXT 2015 Moscow 1/32

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

Embed Size (px)

Citation preview

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

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

Андрей Акиньшин, JetBrains

.NEXT 2015 Moscow

1/32

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

Про что будем разговаривать?

Не будет:• Универсальных способов оптимизации• Скучной теории• Подробного устройства .NET, GC, JIT, CPU

Будет:• Много весёлых историй1

2/32

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

Про что будем разговаривать?

Не будет:• Универсальных способов оптимизации• Скучной теории• Подробного устройства .NET, GC, JIT, CPU

Будет:• Много весёлых историй1

2/32

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

Про что будем разговаривать?

Не будет:• Универсальных способов оптимизации• Скучной теории• Подробного устройства .NET, GC, JIT, CPU

Будет:• Много весёлых историй1

1Все истории основаны на реальных событиях2/32

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

Roslyn

3/32 Roslyn

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

Roslyn

Задачка:

void Print(string format,params object[] args)

{// ...

}

Print("DotNext"); // Будет ли аллокация?

old csc.exe Roslynnew object[0] System.Array.Empty<T>1

4/32 Roslyn

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

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

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

Boxing

5/32 Boxing

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

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

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

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

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

Примеры

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

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

Примеры

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

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

Иногда сможет

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

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

А иногда нет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

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

GC

10/32 GC

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

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

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

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

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

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

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

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

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

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

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

OS

13/32 OS

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

OS

14/32 OS

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

Guid

15/32 OS

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

Benchmarks

16/32 Benchmarks

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

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

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

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

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

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

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

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

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

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

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

RyuJIT

18/32 RyuJIT

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

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

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

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

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

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

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

RyuJIT

Time PercentComputeHash 4047ms 100%RotateRight 1487ms 37%

20/32 RyuJIT

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

RyuJIT

21/32 RyuJIT

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Memory

25/32 Memory

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

stackalloc

26/32 stackalloc

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

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

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

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

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

Branch prediction

28/32 Branch prediction

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

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

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

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

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

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

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

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

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

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

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

Методическая литература

31/32

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

Вопросы?

Андрей Акиньшин, JetBrainshttp://aakinshin.net

https://github.com/AndreyAkinshinhttps://twitter.com/andrey_akinshin

[email protected]

32/32