Я знаю, что этот вопрос был задан много людей , а некоторые даже говорят
Итак, во-первых (FirstOrDefault (предикат)) лучше с точки зрения производительности 1
и я понимаю, что еще один вызов метода должен быть немного медленнее, это просто интуиция, которая у меня есть. Тем не менее, я решил провести некоторые тесты, чтобы доказать, что я прав, и мальчик мало что знал.
Вот что я получил в результате выполнения моих тестов:
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363
Intel Core i7-3630QM CPU 2.40GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.101
[Host] : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
Job-XMZTSC : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT
Runtime=.NET 4.7.2
Method N Mean Error StdDev Ratio RatioSD
WherePlusFirstOrDefaultArray 10000 31.44 us 0.288 us 0.270 us 0.40 0.00
FirstOrDefaultArray 10000 78.47 us 0.679 us 0.635 us 1.00 0.00
WherePlusFirstOrDefaultList 10000 54.27 us 1.070 us 1.099 us 0.69 0.02
FirstOrDefaultList 10000 100.84 us 1.722 us 1.611 us 1.29 0.02
WherePlusFirstOrDefaultArray 100000 325.41 us 4.840 us 4.527 us 0.39 0.01
FirstOrDefaultArray 100000 829.85 us 16.513 us 15.446 us 1.00 0.00
WherePlusFirstOrDefaultList 100000 558.10 us 5.507 us 5.151 us 0.67 0.01
FirstOrDefaultList 100000 1,026.93 us 17.648 us 16.508 us 1.24 0.02
WherePlusFirstOrDefaultArray 1000000 3,255.46 us 9.615 us 7.507 us 0.40 0.01
FirstOrDefaultArray 1000000 8,134.15 us 108.425 us 101.420 us 1.00 0.00
WherePlusFirstOrDefaultList 1000000 5,477.63 us 70.584 us 66.024 us 0.67 0.01
FirstOrDefaultList 1000000 9,987.54 us 64.239 us 60.089 us 1.23 0.02
Не только Where(predicate).FirstOrDefault()
был быстрее, но с каким запасом.
Это мой тестовый код, использующий BenchmarkDotNet
[SimpleJob(RuntimeMoniker.Net472)]
public class Benchmarks
{
private int[] _array;
private List<int> _list;
[Params(10000, 100000, 1000000)]
public int N;
[GlobalSetup]
public void Setup()
{
_array = new int[N];
_list = new List<int>(N);
_array = Enumerable
.Repeat(0, N - 1).ToArray();
_list = Enumerable
.Repeat(0, N - 1).ToList();
_array[N - 2] = 7;
_list[N - 2] = 7;
}
[Benchmark]
public int WherePlusFirstOrDefaultArray()
{
var seven = _array.Where(n => n == 7).FirstOrDefault();
return seven;
}
[Benchmark(Baseline = true)]
public int FirstOrDefaultArray()
{
var seven = _array.FirstOrDefault(n => n == 7);
return seven;
}
[Benchmark]
public int WherePlusFirstOrDefaultList()
{
var seven = _list.Where(n => n == 7).FirstOrDefault();
return seven;
}
[Benchmark]
public int FirstOrDefaultList()
{
var seven = _list.FirstOrDefault(n => n == 7);
return seven;
}
}
Поскольку я был ошеломлен результатами, у меня не было другого выбора, кроме как спросите вас, ребята, что я делаю не так или я что-то упустил?
РЕДАКТИРОВАТЬ:
Я добавил тесты для структуры массива и списка, чтобы ребята думали, что это может быть из-за List
.
РЕДАКТИРОВАТЬ 2: Сага продолжается, и я думаю, что я ближе к ответу. Добавление аппаратного счетчика к моему тесту дало следующие интересные результаты:
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363
Intel Core i7-3630QM CPU 2.40GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.101
[Host] : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
Job-ZTIMEH : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT
Runtime=.NET 4.7.2
Method N Mean Error StdDev Ratio RatioSD CacheMisses/Op BranchMispredictions/Op
WherePlusFirstOrDefaultArray 1000000 3.222 ms 0.0224 ms 0.0210 ms 0.39 0.01 885 327
FirstOrDefaultArray 1000000 8.166 ms 0.1992 ms 0.1863 ms 1.00 0.00 1,795 810
WherePlusFirstOrDefaultList 1000000 5.564 ms 0.1066 ms 0.1228 ms 0.68 0.02 1,051 503
FirstOrDefaultList 1000000 10.161 ms 0.1816 ms 0.1699 ms 1.24 0.03 3,451 1,442
Почему-то я до сих пор не могу объяснить себе, почему метод FirstOrDefault(predicate)
дает от 2 до 3 раз больше ошибочные прогнозы ветвлений и c кешей меньше, чем Where(predicate).FirstOrDefault()
, конечно, это должно сыграть небольшую роль в результатах, которые я наблюдаю ранее.
Также, одна любопытная вещь, если вы посмотрите на FirstOrDefaultArray
и FirstOrDefaultList
Результаты и сравнивая их, вы увидите, что список на 24% медленнее, но сборки, сгенерированные этими методами, идентичны мне: https://www.diffchecker.com/WSjAQlet (я удалил адреса памяти из инструкций.)