Comments 16
[Config(typeof(BenchmarkConfig))]
public class Benchmark
{
readonly int[] Values = Enumerable.Range(0, 5000).Select(i => i).ToArray();
const int OuterCount = 1000;
[Benchmark]
public int LinqBench()
{
var value = 0;
for (var i = 0; i < OuterCount; i++)
value |= Values.Where(j => j % 2 == 0).Sum();
return value;
}
[Benchmark]
public int ForBench()
{
var value = 0;
for (var i = 0; i < OuterCount; i++)
{
var sum = 0;
foreach (var val in Values)
if (val % 2 == 0)
sum += val;
value |= sum;
}
return value;
}
}
Intel Core i7-5820K CPU 3.30GHz (Broadwell), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=3.1.300
[Host]: .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
MediumRun: .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
Job=MediumRun BuildConfiguration=Release Toolchain=.NET Core 3.1
IterationCount=15 LaunchCount=2 WarmupCount=10
| Method | Mean | Error | StdDev |
|---------- |----------:|----------:|----------:|
| LinqBench | 22.458 ms | 0.0325 ms | 0.0476 ms |
| ForBench | 3.865 ms | 0.0186 ms | 0.0273 ms |
но в разработке фреймворков и алгоритмов Linq однозначно нет
Про "алгоритмы" не знаю (хотя правильно примененный LINQ не обязательно меняет алгоритмическую сложность), а вот во фреймворках — да пожалуйста, сколько угодно. Главное, понимать, зачем.
Я наблюдаю некоторую разницу между утверждениями "в разработке фреймворков [...] Linq однозначно нет" и "Фреймворки разные бывают [...] но точно не performance-critical"
Где-нибудь в ядре asp.net core или roslyn Linq врядли используется хоть в одном методе, который десятки тысяч раз в секунду выполняется.
И много таких методов в среднестатистическом фреймворке? А так-то в asp.net Core больше тысячи упоминаний System.Linq (да, я вижу, что многие из них — в тестах и билдере).
Но вы же не станете спорить, что Linq добавляет некоторый overhead
Не буду. Абстракции вообще обычно добавляют оверхед.
если в ядре какого-нибудь фреймворка метод выполняться будет сотни миллионов раз, и написание + пары строк для простого цикла сэкономит хотя бы 1% производительности, то можно пожертвовать выразительностью?
Если. Сколько раз типичный программист сталкивается с подобными методами за свою карьеру?
В любом случае, нужно просто правильно выбирать инструмент для решения задачи, а не топить за Linq добро/зло и разоблачающие статьи на эту тему писать.
Ну так чтобы правильно выбирать инструмент, нужно понимать, где он добро. А не писать вещи типа "в разработке фреймворков [...] однозначно нет".
[Benchmark]
public int LinqBench()
{
var value = 0;
for (var i = 1; i < OuterCount; i++)
{
value |= Values.Where(j => j % i == 0).Sum();
}
return value;
}
[Benchmark]
public int ForBench()
{
var value = 0;
for (var i = 1; i < OuterCount; i++)
{
var sum = 0;
foreach (var val in Values)
if (val % i == 0)
sum += val;
value |= sum;
}
return value;
}
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.836 (1909/November2018Update/19H2)
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=3.1.202
[Host]: .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
DefaultJob: .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
| Method | Mean | Error | StdDev |
|---------- |---------:|---------:|---------:|
| LinqBench | 17.81 ms | 0.350 ms | 0.535 ms |
| ForBench | 13.34 ms | 0.261 ms | 0.268 ms |
Первый вариант слишком легко оптимизировать, достаточно сделать два последовательных цикла вместо вложенных, но это уже никакого отношения к linq vs обычный цикл не имеет.
На самом деле в данном случае цикл выигрывает у linq варианта потому, что обращается к элементам массива по индексу, что значительно быстрее, чем итератор. Если тип у Values будет какой-нибудь IReadOnlyCollection, результаты сравняются и linq даже может чуточку выигрывать.
Первый вариант на моей машине, поменял тип на IReadOnlyCollection:
| Method | Mean | Error | StdDev |
|---------- |---------:|---------:|---------:|
| LinqBench | 23.65 ms | 0.465 ms | 0.886 ms |
| ForBench | 25.52 ms | 0.474 ms | 0.633 ms |
Так что если к вам данные приходят в виде IEnumerable, IReadOnlyCollection или еще в чем-то, где нельзя обратиться по индексу, то отказываться от linq нет особого смысла.
Посему, я часто пишуПервый абзац
О бедном LINQ’е замолвите слово