LINQ Where 与 For 循环实现

LINQ Where vs For Loop implementation

我正在尝试了解 C# LINQ 实现及其针对 FOR 和 FOREACH 循环的性能。

在我看到的每个帖子中,使用 for 循环实现比 LINQ 实现要好得多(就性能而言)。 Example1, Example2, Example3

然而,我正尝试使用我自己的 POC 来查看我是否可以优化 GroupByWhere 操作,但我看到了相反的结果。你能告诉我我的实现是否可以优化得更好吗?

        //Where Implementation (Main Call)
        var students = createStudentList();
        var stopwatch1 = new Stopwatch();
        stopwatch1.Start();
        var y = students.Where(s=> s.age == 32);
        foreach(var entry in y){}
        stopwatch1.Stop();

        Console.WriteLine("1) TICKS ELAPSED WHERE: " + stopwatch1.ElapsedTicks);
        Console.WriteLine("1) MILLISECONDS  WHERE: " + stopwatch1.ElapsedMilliseconds);

        var stopwatch2 = new Stopwatch();
        stopwatch2.Start();
        var y2 = WhereManual(students);
        foreach(var entry in y2){}
        stopwatch2.Stop();

        Console.WriteLine("2) TICKS ELAPSED FOR: " + stopwatch2.ElapsedTicks);
        Console.WriteLine("2) MILLISECONDS  FOR: " + stopwatch2.ElapsedMilliseconds);



    public List<Student> WhereManual(List<Student> students){
        var filteredList = new List<Student>();
        for(var i = 0; i < students.Count(); i++){
            var student = students[i];
            if(student.age == 32){
                filteredList.Add(student);
            }
        }
        return filteredList;
    }

输出:

1) TICKS ELAPSED WHERE: 389478
1) MILLISECONDS WHERE: 38
2) TICKS ELAPSED FOR: 654023
2) MILLISECONDS FOR: 65

对于 GroupBy 我有

//GroupBy Implementation (Main Call)
        var students = createStudentList();
        var stopwatch1 = new Stopwatch();
        stopwatch1.Start();
        var y = students.GroupBy(s => s.age);
        foreach(var entry in y){}
        stopwatch1.Stop();

        Console.WriteLine("1) TICKS ELAPSED GROUPBY: " + stopwatch1.ElapsedTicks);
        Console.WriteLine("1) MILLISECONDS GROUPBY: " + stopwatch1.ElapsedMilliseconds);

        var stopwatch2 = new Stopwatch();
        stopwatch2.Start();
        var y2 = dictOperation(students);
        foreach(var entry in y2){}
        stopwatch2.Stop();

        Console.WriteLine("2) TICKS ELAPSED FOR: " + stopwatch2.ElapsedTicks);
        Console.WriteLine("2) MILLISECONDS  FOR: " + stopwatch2.ElapsedMilliseconds);


    public List<Student> GetStudent(Dictionary<int, List<Student>> dict, int age){
        List<Student> dictStudent;
        return dict.TryGetValue(age, out dictStudent) ? dictStudent : null;
    }

    public Dictionary<int, List<Student>> dictOperation(List<Student> students){
        var dict = new Dictionary<int, List<Student>>();
        for(var i = 0; i < students.Count(); i++){
            var student = students[i];
            var studentAge = student.age;
            var dictStudent = GetStudent(dict, studentAge);
            if(dictStudent == null)
            {
                dict.Add(studentAge, new List<Student>(){student});
            } 
            else
            {
                dictStudent.Add(student);
            }
        }
        return dict;
    }

这是输出:

1) TICKS ELAPSED GROUPBY: 865702
1) MILLISECONDS GROUPBY: 86
2) TICKS ELAPSED FOR: 1364863
2) MILLISECONDS FOR: 1.36

答案不多,但既然我玩了一点,我不妨分享一下。

我没有花太多时间查看 GroupBy 比较,因为使用的类型差异很大,可能是瓶颈,而且我对 IGrouping 不够熟悉,无法创建立即进行新测试。

我发现如果你使用 List.Count 属性 而不是 List.Count() 扩展方法,它节省了足够的时间(迭代 1000000 项目)来制作手动代码比 Linq 更快。此外,通过删除分配 var student = students[i];:

节省了更多的毫秒数
public class Student { public string Name { get; set; } public int Age { get; set; } }

public class Program
{
    public static List<Student> Students = new List<Student>();

    public static void CreateStudents()
    {
        for (var i = 0; i < 1000000; i++)
        {
            Students.Add(new Student {Name = $"Student{i}", Age = i});
        }
    }

    public static List<Student> WhereManualOriginal(List<Student> students)
    {
        var filteredList = new List<Student>();

        for (var i = 0; i < students.Count(); i++)
        {
            var student = students[i];

            if (student.Age == 32)
            {
                filteredList.Add(student);
            }
        }

        return filteredList;
    }

    public static List<Student> WhereManualNew(List<Student> students)
    {
        var filteredList = new List<Student>();

        for (var i = 0; i < students.Count; i++)
        {
            if (students[i].Age == 32)
            {
                filteredList.Add(students[i]);
            }
        }

        return filteredList;
    }

    public static long LinqWhere()
    {
        var sw = Stopwatch.StartNew();
        var items = Students.Where(s => s.Age == 32);
        foreach (var item in items) { }
        sw.Stop();
        return sw.ElapsedTicks;
    }

    public static long ManualWhere()
    {
        var sw = Stopwatch.StartNew();
        var items = WhereManualOriginal(Students);
        foreach (var item in items) { }
        sw.Stop();
        return sw.ElapsedTicks;
    }

    public static long NewManualWhere()
    {
        var sw = Stopwatch.StartNew();
        var items = WhereManualNew(Students);
        foreach (var item in items) { }
        sw.Stop();
        return sw.ElapsedTicks;
    }


    public static void Main()
    {
        // Warmup stuff
        CreateStudents();
        WhereManualOriginal(Students);
        WhereManualNew(Students);
        Students.Where(s => s.Age == 32).ToList();
        var linqResults = new List<long>();
        var manualResults = new List<long>();
        var newManualResults = new List<long>();

        for (int i = 0; i < 100; i++)
        {
            newManualResults.Add(NewManualWhere());
            manualResults.Add(ManualWhere());
            linqResults.Add(LinqWhere());
        }

        Console.WriteLine("Linq where ......... " + linqResults.Average());
        Console.WriteLine("Manual where ....... " + manualResults.Average());
        Console.WriteLine("New Manual where ... " + newManualResults.Average());

        GetKeyFromUser("\nDone! Press any key to exit...");
    }
}

输出