M
M
Mikhail Emelyanov2021-07-29 13:49:27
C++ / C#
Mikhail Emelyanov, 2021-07-29 13:49:27

Why is the "manual" foreach three times faster than LINQ?

I study LINQ, for some reason it works a little slowly.

I create a dictionary with random data:

public static class GenerateCollection
    {
        private static readonly Random random = new();

        public static Dictionary<string, int> Get(int workersNum)
        {
            Dictionary<string, int> stuff = new();

            while (stuff.Count < workersNum)
            {
                var workerName = Path.GetRandomFileName()[..4];
                var workerGrade = random.Next(1, 100);

                stuff[workerName] = workerGrade;
            }

            return stuff;
        }
    }


I take two methods, one calculates the average using LINQ, the second - "manually", using foreach:

public static class LinqMethods
    {
        public static double Average(Dictionary<string, int> stuff) => stuff.Average(stuff => stuff.Value);
    }

and
public static class ManualMethods
    {
        public static double Average(Dictionary<string, int> stuff)
        {
            long sum = 0;

            checked
            {
                foreach (var worker in stuff)
                {
                    sum += worker.Value;
                }
            }

            return (double) sum / stuff.Count;
        }
    }


Compare using BenchmarkDotNet:
public class LinqVsManual
    {
        private Dictionary<string, int> _stuffA = new();

        [Params(1_000, 10_000, 100_000)]
        public int WorkersNum { get; set; }

        [IterationSetup]
        public void Setup()
        {
            _stuffA = GenerateCollection.Get(WorkersNum);
        }

        [Benchmark]
        public double ManualAverage()
        {
            return ManualMethods.Average(_stuffA);
        }

        [Benchmark]
        public double LinqAverage()
        {
            return LinqMethods.Average(_stuffA);
        }
    }

    internal class Program
    {
        private static void Main()
        {
            BenchmarkRunner.Run<LinqVsManual>();
        }
    }


I get:
|        Method | WorkersNum |         Mean |
|-------------- |----------- |-------------:|
| ManualAverage |       1000 |     2.879 us |
|   LinqAverage |       1000 |    12.792 us | 
| ManualAverage |      10000 |    28.673 us | 
|   LinqAverage |      10000 |   115.260 us | 
| ManualAverage |     100000 |   391.715 us |
|   LinqAverage |     100000 | 1,158.016 us |


I think it's not LINQ that is that slow, I made a mistake somewhere, because I actually repeat the code from github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq. But where?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
F
Foggy Finder, 2021-07-29
@sepulkary

Now you're comparing the selector (converting function) version with a regular loop, instead it's better to compare the "pure" version:

public static double Average(Dictionary<string, int> stuff) => stuff.Values.Average();

then the difference will not be so significant.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question