C# 并行 Vs.线程代码性能 [英] C# Parallel Vs. Threaded code performance

查看:62
本文介绍了C# 并行 Vs.线程代码性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在测试 System.Threading.Parallel 与 Threading 的性能,我很惊讶地看到 Parallel 比线程花费更长的时间来完成任务.我确定这是由于我对 Parallel 的了解有限,我才刚刚开始阅读.

I've been testing the performance of System.Threading.Parallel vs a Threading and I'm surprised to see Parallel taking longer to finish tasks than threading. I'm sure it's due to my limited knowledge of Parallel, which I just started reading up on.

我想我会分享一些片段,如果有人可以向我指出并行代码的运行速度比线程代码慢.还尝试运行相同的比较来查找素数,发现并行代码完成得比线程代码晚得多.

I thought i'll share few snippets and if anyone can point out to me paralle code is running slower vs threaded code. Also tried to run the same comparison for finding prime numbers and found parallel code finishing much later than threaded code.

public class ThreadFactory
{
    int workersCount;
    private List<Thread> threads = new List<Thread>();

    public ThreadFactory(int threadCount, int workCount, Action<int, int, string> action)
    {
        workersCount = threadCount;

        int totalWorkLoad = workCount;
        int workLoad = totalWorkLoad / workersCount;
        int extraLoad = totalWorkLoad % workersCount;

        for (int i = 0; i < workersCount; i++)
        {
            int min, max;
            if (i < (workersCount - 1))
            {
                min = (i * workLoad);
                max = ((i * workLoad) + workLoad - 1);
            }
            else
            {
                min = (i * workLoad);
                max = (i * workLoad) + (workLoad - 1 + extraLoad);
            }
            string name = "Working Thread#" + i; 

            Thread worker = new Thread(() => { action(min, max, name); });
            worker.Name = name;
            threads.Add(worker);
        }
    }

    public void StartWorking()
    {
        foreach (Thread thread in threads)
        {
            thread.Start();
        }

        foreach (Thread thread in threads)
        {
            thread.Join();
        }
    }
}

这是程序:

Stopwatch watch = new Stopwatch();
watch.Start();
int path = 1;

List<int> numbers = new List<int>(Enumerable.Range(0, 10000));

if (path == 1)
{
    Parallel.ForEach(numbers, x =>
    {
        Console.WriteLine(x);
        Thread.Sleep(1);

    });
}
else
{
    ThreadFactory workers = new ThreadFactory(10, numbers.Count, (min, max, text) => {

        for (int i = min; i <= max; i++)
        {
            Console.WriteLine(numbers[i]);
            Thread.Sleep(1);
        }
    });

    workers.StartWorking();
}

watch.Stop();
Console.WriteLine(watch.Elapsed.TotalSeconds.ToString());

Console.ReadLine();

更新:

考虑锁定:我尝试了以下代码段.同样的结果,Parallel 似乎完成得慢得多.

Taking Locking into consideration: I tried the following snippet. Again the same results, Parallel seems to finish much slower.

路径 = 1;cieling = 10000000;

path = 1; cieling = 10000000;

    List<int> numbers = new List<int>();

    if (path == 1)
    {
        Parallel.For(0, cieling, x =>
        {
            lock (numbers)
            {
                numbers.Add(x);    
            }

        });
    }

    else
    {
        ThreadFactory workers = new ThreadFactory(10, cieling, (min, max, text) =>
        {

            for (int i = min; i <= max; i++)
            {
                lock (numbers)
                {
                    numbers.Add(i);    
                }                       

            }
        });

        workers.StartWorking();
    }

更新 2:只是快速更新我的机器有四核处理器.所以Parallel有4个内核可用.

Update 2: Just a quick update that my machine has Quad Core Processor. So Parallel have 4 cores available.

推荐答案

参考博文 作者:Reed Copsey Jr:

Refering to a blog post by Reed Copsey Jr:

Parallel.ForEach 有点复杂,但是.使用通用 IEnumerable 时,处理所需的项数事先未知,必须在运行时发现.此外,由于我们无法直接访问每个元素,调度程序必须枚举集合来处理它.由于 IEnumerable 不是线程安全的,它必须在枚举元素时锁定元素,为每个要处理的块创建临时集合,并安排它.

Parallel.ForEach is a bit more complicated, however. When working with a generic IEnumerable, the number of items required for processing is not known in advance, and must be discovered at runtime. In addition, since we don’t have direct access to each element, the scheduler must enumerate the collection to process it. Since IEnumerable is not thread safe, it must lock on elements as it enumerates, create temporary collections for each chunk to process, and schedule this out.

锁定和复制可能会使 Parallel.ForEach 花费更长的时间.此外,ForEach 的分区和调度程序可能会影响并产生开销.我测试了你的代码并增加了每个任务的睡眠,然后结果更接近,但仍然 ForEach 更慢.

The locking and copying could make Parallel.ForEach take longer. Also partitioning and the scheduler of ForEach could impact and give overhead. I tested your code and increased the sleep of each task, and then the results are closer, but still ForEach is slower.

我在执行循环中添加了以下内容:

I added the following to the execution loops:

if (Thread.CurrentThread.ManagedThreadId > maxThreadId)
   maxThreadId = Thread.CurrentThread.ManagedThreadId;

这在我的机器上显示的是,与使用当前设置的另一个线程相比,它使用 ForEach 减少了 10 个线程.如果您希望 ForEach 中有更多线程,则必须摆弄 ParallelOptions 和 Scheduler.

What this shows on my machine is that it uses 10 threads less with ForEach, compared to the other one with the current settings. If you want more threads out of ForEach, you would have to fiddle around with ParallelOptions and the Scheduler.

参见 是否 Parallel.ForEach 限制活动线程的数量?

这篇关于C# 并行 Vs.线程代码性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆