Здравствуйте, друзья.
Мне, если честно, немножко лень переводить то что я на stackoverflow нашёл, а доделать проект очень хочется.
Так вот, вопрос простой: есть функция сложная, в ней есть дублирование кода (4...6 инструкций с ветвлением). Я, как старый VB6 программист, разделил бы функцию на несколько и сделал их вызов, не раздумывая. Но как человека современного меня интересует, а не сделать-ли всё на лямбдах. Причём лямбды меня прельщают ещё тем, что у них есть доступ ко всем локальным переменным родительской функции, и, значит, передача параметров не нужна.
Собственно вопрос: а что работает быстрее? — Вызов нескольких маленьких функций или вызов и создание лямбд?
Заранее спасибо.
Наука изощряет ум; ученье вострит память.
(c) Козьма Прутков
Здравствуйте, ZAMUNDA, Вы писали:
ZAM>Собственно вопрос: а что работает быстрее? — Вызов нескольких маленьких функций или вызов и создание лямбд?
Смотря каких лямбд
http://rsdn.ru/forum/flame.comp/4098976.1Автор: Pavel Dvorkin
Дата: 29.12.10
В общем случае вызов функций быстрее создания лямбд, т.к. для лямбд создаются временные объекты.
Но в частных случаях возможны нюансы.
Одиночное наследование — это всего лишь частный случай множественного наследования.
Здравствуйте, ZAMUNDA, Вы писали:
ZAM>Собственно вопрос: а что работает быстрее? — Вызов нескольких маленьких функций или вызов и создание лямбд?
Сомневаешься —
| проверяй: |
| using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace ConsoleApplication1
{
interface IProgram
{
int CallInterface(int a);
int CallInterface<T>(int a);
}
interface IProgram<T>
{
T CallInterface(T a);
}
class Program2 : Program
{
public override int CallVirtual(int a)
{
return a + 1;
}
public override int CallInterface(int a)
{
return a + 1;
}
public override int CallInterface<T>(int a)
{
return a + 1;
}
}
class Program : IProgram, IProgram<int>
{
private static int Call(int a)
{
return a + 1;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int CallNoInline(int a)
{
return a + 1;
}
private static int Call<T>(int a)
{
return a + 1;
}
private int CallInst(int a)
{
return a + 1;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private int CallInstNoInline(int a)
{
return a + 1;
}
private int CallInst<T>(int a)
{
return a + 1;
}
public virtual int CallVirtual(int a)
{
return a + 1;
}
public virtual int CallInterface(int a)
{
return a + 1;
}
public virtual int CallInterface<T>(int a)
{
return a + 1;
}
// some work : i=>i+1
static void Main(string[] args)
{
int count = 10 * 1000 * 1000;
Measure("raw", () =>
{
int a = 0;
for (int i = 0; i < count; i++) a = a + 1;
return count;
});
Console.WriteLine();
Measure("call", () =>
{
int a = 0;
for (int i = 0; i < count; i++) a = Call(a);
return count;
});
Measure("generic call", () =>
{
int a = 0;
for (int i = 0; i < count; i++) a = Call<object>(a);
return count;
});
Measure("instance call", () =>
{
int a = 0;
var p = new Program();
for (int i = 0; i < count; i++) a = p.CallInst(a);
return count;
});
Measure("instance generic call", () =>
{
int a = 0;
var p = new Program();
for (int i = 0; i < count; i++) a = p.CallInst<object>(a);
return count;
});
Measure("call (no inline)", () =>
{
int a = 0;
for (int i = 0; i < count; i++) a = CallNoInline(a);
return count;
});
Measure("instance call (no inline)", () =>
{
int a = 0;
var p = new Program();
for (int i = 0; i < count; i++) a = p.CallInstNoInline(a);
return count;
});
Console.WriteLine();
Measure("instance virtual call", () =>
{
int a = 0;
var p = new Program();
for (int i = 0; i < count; i++) a = p.CallVirtual(a);
return count;
});
Measure("derived virtual call", () =>
{
int a = 0;
var p = new Program2();
for (int i = 0; i < count; i++) a = p.CallVirtual(a);
return count;
});
Measure("interface call", () =>
{
int a = 0;
IProgram p = new Program();
for (int i = 0; i < count; i++) a = p.CallInterface(a);
return count;
});
Measure("derived interface call", () =>
{
int a = 0;
IProgram p = new Program2();
for (int i = 0; i < count; i++) a = p.CallInterface(a);
return count;
});
Measure("generic interface call", () =>
{
int a = 0;
IProgram<int> p = new Program();
for (int i = 0; i < count; i++) a = p.CallInterface(a);
return count;
});
Measure("derived generic interface call", () =>
{
int a = 0;
IProgram<int> p = new Program2();
for (int i = 0; i < count; i++) a = p.CallInterface(a);
return count;
});
Console.WriteLine();
Measure(" interface generic call", () =>
{
int a = 0;
IProgram p = new Program();
for (int i = 0; i < count; i++) a = p.CallInterface<object>(a);
return count;
});
Measure("derived interface generic call", () =>
{
int a = 0;
IProgram p = new Program2();
for (int i = 0; i < count; i++) a = p.CallInterface<object>(a);
return count;
});
Console.WriteLine();
Measure("lambda (cached)", () =>
{
int a1 = 0;
Func<int, int> x = a => a + 1;
for (int i = 0; i < count; i++) a1 = x(a1);
return count;
});
Measure("lambda (new)", () =>
{
int a1 = 0;
for (int i = 0; i < count; i++)
{
Func<int, int> x = a => a + 1;
a1 = x(a1);
};
return count;
});
Measure("lambda (closure)", () =>
{
int a1 = 0;
var t = 0;
for (int i = 0; i < count; i++)
{
t = 1;
Func<int, int> x = a => a + t;
a1 = x(a1);
};
return count;
});
Measure("lambda (closure local)", () =>
{
int a1 = 0;
for (int i = 0; i < count; i++)
{
var t = 1;
Func<int, int> x = a => a + t;
a1 = x(a1);
};
return count;
});
Measure("Func (cached)", () =>
{
int a = 0;
Func<int, int> x = Call;
for (int i = 0; i < count; i++) a = x(a);
return count;
});
Measure("Func (new)", () =>
{
int a = 0;
for (int i = 0; i < count; i++)
{
Func<int, int> x = Call;
a = x(a);
};
return count;
});
Console.Write("Done...");
Console.ReadKey();
}
static void Measure(string name, Func<long> callback)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
var sw = Stopwatch.StartNew();
var tmp = callback();
sw.Stop();
Console.WriteLine("{0,30}: {1,5}ms, ips: {2,16:N} : {3,9}", name, sw.ElapsedMilliseconds, tmp / sw.Elapsed.TotalSeconds, tmp);
}
}
}
|
| |
.net 4.5.1, release, no debugger, any cpu:
raw: 2ms, ips: 3 430 884 825,20 : 10000000
call: 2ms, ips: 3 462 843 687,24 : 10000000
generic call: 3ms, ips: 3 305 566 574,11 : 10000000
instance call: 3ms, ips: 3 290 772 673,42 : 10000000
instance generic call: 2ms, ips: 3 422 782 037,24 : 10000000
call (no inline): 20ms, ips: 496 428 199,11 : 10000000
instance call (no inline): 21ms, ips: 463 226 744,86 : 10000000
instance virtual call: 19ms, ips: 515 820 205,71 : 10000000
derived virtual call: 19ms, ips: 503 443 553,91 : 10000000
interface call: 26ms, ips: 383 765 197,10 : 10000000
derived interface call: 23ms, ips: 429 856 084,18 : 10000000
generic interface call: 25ms, ips: 385 528 791,29 : 10000000
derived generic interface call: 25ms, ips: 386 239 074,26 : 10000000
interface generic call: 106ms, ips: 93 702 446,01 : 10000000
derived interface generic call: 105ms, ips: 94 401 250,63 : 10000000
lambda (cached): 34ms, ips: 290 324 641,01 : 10000000
lambda (new): 43ms, ips: 231 436 479,94 : 10000000
lambda (closure): 32ms, ips: 311 350 920,51 : 10000000
lambda (closure local): 120ms, ips: 82 701 015,15 : 10000000
Func (cached): 34ms, ips: 290 684 154,23 : 10000000
Func (new): 74ms, ips: 133 860 386,29 : 10000000
Done...
Маленький квестЪ: поменять в "Func (cached)" строчку
Func<int, int> x = Call;
так, чтобы стоимость вызова приблизилась к "call (no inline)"
P.S. Когда меряете — следите за опечатками! Одна буква (у меня это было "a1 = x(i)" вместо "a1 = x(a1)"), и на основе неправильных тестов делаются очень неправильные выводы
Здравствуйте, Sinix, Вы писали:
S>Маленький квестЪ: поменять в "Func (cached)" строчку
S>S> Func<int, int> x = Call;
S>
S>так, чтобы стоимость вызова приблизилась к "call (no inline)"
Func<int, int> x = new Program().CallInst
Здравствуйте, vorona, Вы писали:
S>>так, чтобы стоимость вызова приблизилась к "call (no inline)"
V>
Func<int, int> x = new Program().CallInst
Именно
В referencesource есть
подсказка:
// _methodPtr is a pointer to the method we will invoke
// It could be a small thunk if this is a static or UM call <-- here
#if !FEATURE_CORECLR
[System.Runtime.ForceTokenStabilization]
#endif //!FEATURE_CORECLR
[System.Security.SecurityCritical]
internal IntPtr _methodPtr;
// In the case of a static method passed to a delegate, this field stores
// whatever _methodPtr would have stored: and _methodPtr points to a <-- and here
// small thunk which removes the "this" pointer before going on
// to _methodPtrAux.
#if !FEATURE_CORECLR
[System.Runtime.ForceTokenStabilization]
#endif //!FEATURE_CORECLR
[System.Security.SecurityCritical]
internal IntPtr _methodPtrAux;
Этот нюанс кстати учтён в c#6, правда,
не с первой попытки