Я думаю, что есть небольшая разница в скорости, но первое, что я хочу сказать, - это то, что я хотел бы улучшить в вашем тесте. При построении этих микро-тестов , важно всегда соблюдать несколько правил:
- При хронометраже используйте секундомер вместо DateTime - он имеет более высокое разрешение и точнее.
- Всегда убедитесь, что вы прогреваете весь тестируемый код один раз перед его запуском. В противном случае первый бит кода будет иметь тенденцию работать медленнее, поскольку он будет стоить большую часть JIT'ing
- Всегда запускайте тест несколько раз (вы сделали это в своем примере).
Если я переделываю ваш тест, я получаю что-то вроде этого:
public void Go()
{
// warmup
Test_Equality();
Test_Lambda();
// timed tests
Console.WriteLine(Test_Equality() + " ms");
Console.WriteLine(Test_Lambda() + " ms");
}
public long Test_Lambda()
{
var units1 = GetUnits();
var stopWatch1 = new Stopwatch();
stopWatch1.Start();
MyUnit item1 = units1.testme<MyUnit>(u => u.Id == 999999);
return stopWatch1.ElapsedMilliseconds;
}
public long Test_Equality()
{
var units2 = GetUnits();
var stopWatch2 = new Stopwatch();
stopWatch2.Start();
MyUnit item2;
foreach (MyUnit unit in units2)
{
if (unit.Id == 999999)
{
item2 = unit;
break;
}
}
return stopWatch2.ElapsedMilliseconds;
}
Я запускаю это, я получаю цифры, примерно такие:
Test_Lambda: 68 ms
Test_Equality: 53 ms
В целом, я ожидал бы, что при вызове делегата / лямбда-версии по сравнению с собственным вызовом произойдет небольшое снижение производительности, точно так же, как будет небольшое снижение производительности при вызове метода через делегат, а не при вызове метод напрямую. За кулисами компилятор генерирует дополнительный код для поддержки этой лямбда-версии вашего теста.
В конечном итоге оно генерирует что-то в соответствии с this:
public class PossibleLambdaImpl
{
public bool Comparison(MyUnit myUnit)
{
return myUnit.Id == 9999999;
}
}
Следовательно, ваш лямбда-тест на самом деле вызывает метод для класса, сгенерированного компилятором, каждый раз, когда он оценивает.
Фактически - когда я изменяю ваш тест на равенство, чтобы вместо этого создать вышеупомянутый класс PossibleLambdaImpl
один раз, и каждый раз вызывать PossibleLambdaImpl.Comparison
каждый раз вокруг цикла, я получаю почти идентичные результаты для лямбда-случая:
public long Test_PossibleLambdaImpl()
{
var units2 = GetUnits();
var stopWatch2 = new Stopwatch();
stopWatch2.Start();
MyUnit item2;
var possibleLambdaImpl = new PossibleLambdaImpl();
foreach (MyUnit unit in units2)
{
if (possibleLambdaImpl.Comparison(unit))
{
item2 = unit;
break;
}
}
return stopWatch2.ElapsedMilliseconds;
}
[Примечание: на этом сайте есть другие, которые знают об этом гораздо больше, чем я, но, грубо говоря, я считаю, что это правильно]
В любом случае следует помнить, что эта разница в производительности ничтожна. Микро-тесты, подобные этому, всегда подчеркивают разницу. В зависимости от вашего теста между ними может быть разница в производительности на 10% -20%, но если ваш реальный код тратит только 0,001% своего времени на выполнение такого рода вызовов (например), то это равносильно крошечной разнице в выполнение кода.