LINQ Where 与 For 循环实现
LINQ Where vs For Loop implementation
我正在尝试了解 C# LINQ 实现及其针对 FOR 和 FOREACH 循环的性能。
在我看到的每个帖子中,使用 for 循环实现比 LINQ 实现要好得多(就性能而言)。 Example1, Example2, Example3
然而,我正尝试使用我自己的 POC 来查看我是否可以优化 GroupBy
和 Where
操作,但我看到了相反的结果。你能告诉我我的实现是否可以优化得更好吗?
//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...");
}
}
输出
我正在尝试了解 C# LINQ 实现及其针对 FOR 和 FOREACH 循环的性能。
在我看到的每个帖子中,使用 for 循环实现比 LINQ 实现要好得多(就性能而言)。 Example1, Example2, Example3
然而,我正尝试使用我自己的 POC 来查看我是否可以优化 GroupBy
和 Where
操作,但我看到了相反的结果。你能告诉我我的实现是否可以优化得更好吗?
//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...");
}
}
输出