简体   繁体   English

如何提高 C# 中的 foreach 循环性能

[英]how to improve foreach loop performance in C#

class Student
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

class Program
{
    private static object _lockObj = new object();

    static void Main(string[] args)
    {

        List<int> collection = Enumerable.Range(1, 100000).ToList();


         
        List<Student> students = new List<Student>(100000);
        var options = new ParallelOptions()
        {
            MaxDegreeOfParallelism = 1
        };
        var sp = System.Diagnostics.Stopwatch.StartNew();
        sp.Start();
        Parallel.ForEach(collection, options, action =>
        {
            lock (_lockObj)
            {
                 var dt = collection.FirstOrDefault(x => x == action);
                 if (dt > 0)
                 {
                    Student student = new Student();
                    student.ID = dt;
                    student.Name = "Zoyeb";
                    student.Email = "ShaikhZoyeb@Gmail.com";

                    students.Add(student);
                    Console.WriteLine(@"value of i = {0}, thread = {1}", 
                    action,Thread.CurrentThread.ManagedThreadId);
                }
            }
        });
        sp.Stop();

        double data = Convert.ToDouble(sp.ElapsedMilliseconds / 1000);
        Console.WriteLine(data);
         
    }
}

I want to loop through 100000 records as quickly as possible i tried foreach loop but it is not quite good for loop through 100000 records then after i tried to implement Parallel.ForEach() that improved my performance, in real scenario i will have collection of Ids and i need to lookup into collection whether id exits or not if exits then add.我想尽快循环遍历 100000 条记录我尝试了 foreach 循环,但是循环遍历 100000 条记录并不是很好,然后在我尝试实现 Parallel.ForEach() 提高了我的性能之后,在实际场景中我将收集Ids 和我需要查找集合是否 id 退出,如果退出然后添加。 performance is hitting in condition when i comment condition it took around 3 seconds to execute and when i uncomment condition it took around 24 seconds so my question is there any way i can boost my performance by looking up id in collection当我评论条件大约需要 3 秒来执行,而当我取消注释条件大约需要 24 秒,所以我的问题是有什么方法可以通过在集合中查找 id 来提高我的性能

         //var dt = collection.FirstOrDefault(x => x == action);
         //if (dt > 0)
         //{
            Student student = new Student();
            student.ID = 1;
            student.Name = "Zoyeb";
            student.Email = "ShaikhZoyeb@Gmail.com";

            students.Add(student);
            Console.WriteLine(@"value of i = {0}, thread = {1}", 
            action,Thread.CurrentThread.ManagedThreadId);
        //}

Your original code is doing a lock inside a Parallel.ForEach .您的原始代码在Parallel.ForEach内进行lock That's essentially taking the parallel code and forcing it to run in series.这本质上是采用并行代码并强制它串行运行。

It takes 40 seconds on my machine.在我的机器上需要 40 秒。

It is really the equivalent of doing this:这实际上相当于这样做:

    foreach (var action in collection)
    {
            var dt = collection.FirstOrDefault(x => x == action);
            if (dt > 0)
            {
                Student student = new Student();
                student.ID = dt;
                student.Name = "Zoyeb";
                student.Email = "ShaikhZoyeb@Gmail.com";

                students.Add(student);
            }
    }

Which also takes 40 seconds.这也需要40秒。

However, if you just do this:但是,如果你这样做:

    foreach (var action in collection)
    {
        Student student = new Student();
        student.ID = action;
        student.Name = "Zoyeb";
        student.Email = "ShaikhZoyeb@Gmail.com";

        students.Add(student);
    }

That takes 1 millisecond to run.运行需要 1毫秒 It's roughly 40,000 times quicker.它大约快 40,000 倍。

In this case you can get much faster loops by iterating your collection once, not in a nested way and not using Parallel.ForEach .在这种情况下,您可以通过迭代一次集合来获得更快的循环,而不是以嵌套方式并且不使用Parallel.ForEach


My ap0ologies for missing that the bit about the id not existing.我很抱歉错过了关于 id 不存在的信息。

Try this:尝试这个:

    HashSet<int> hashSet = new HashSet<int>(collection);

    List<Student> students = new List<Student>(100000);

    var sp = System.Diagnostics.Stopwatch.StartNew();
    sp.Start();
    foreach (var action in collection)
    {
        if (hashSet.Contains(action))
        {
            Student student = new Student();
            student.ID = action;
            student.Name = "Zoyeb";
            student.Email = "ShaikhZoyeb@Gmail.com";

            students.Add(student);
        }
    }
    sp.Stop();

That runs in 3 milliseconds.运行时间为 3 毫秒。

An alternative is to use a join like this:另一种方法是使用这样的join

    foreach (var action in
        from c in collection
        join dt in collection on c equals dt
        select dt)
    {
        Student student = new Student();
        student.ID = action;
        student.Name = "Zoyeb";
        student.Email = "ShaikhZoyeb@Gmail.com";

        students.Add(student);
    }

That runs in 25 milliseconds.运行时间为 25 毫秒。

Problem 1问题 1

You are using a lock instead of a concurrent collection in a parallel foreach.您在并行 foreach 中使用锁而不是并发集合。 Forcing the parallel foreach to wait, to access the lock and thus execution will be one-by-one.强制并行 foreach 等待、访问锁并因此执行将是一个接一个。

Change your list to a ConcurrentBag , and remove the lock from the ParallelForEach将您的列表更改为ConcurrentBag ,并从 ParallelForEach 中删除lock

// using System.Collections.Concurrent; // at the top
var students = new ConcurrentBag<Student>()

Problem 2问题 2

FirstOrDefault() Isn't very performant if you want to select by Id. FirstOrDefault()如果您想按 Id 来 select,则性能不是很好。 Use a Dictionary.使用字典。 Since the dictionary does a Hashmatch its much faster then FirstOrDefault.由于字典执行哈希匹配,因此它比 FirstOrDefault 快得多。 See this questoin.看到这个问题。

Change your collection to be a dictionary:将您的收藏更改为字典:

var collection = Enumerable.Range(1, 100000)
    .ToDictionary(x=> x);

Change the access in your loop to:将循环中的访问更改为:

if(collection.TryGetValue(action, out var dt))
{
  //....
}

Problem 3问题 3

Stopwatch is not a benchmarking tool.秒表不是基准测试工具。 Please use Benchmark.Net or a different library.请使用Benchmark.Net或其他库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM