C# 并行与并行线程代码性能

问题描述 投票:0回答:4

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

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

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();

更新:

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

路径=1; 天花板 = 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: 只是快速更新我的机器有四核处理器。所以并行有 4 个核心可用。

c# parallel-processing pfx
4个回答
4
投票

参考 Reed Copsey Jr 的 博客文章

然而,Parallel.ForEach 有点复杂。使用通用 IEnumerable 时,处理所需的项目数量事先未知,必须在运行时发现。此外,由于我们无法直接访问每个元素,因此调度程序必须枚举集合来处理它。

由于 IEnumerable 不是线程安全的,因此它必须在枚举时锁定元素,为每个要处理的块创建临时集合,并将其调度出去

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

[编辑 - 更多研究]

我将以下内容添加到执行循环中:

if (Thread.CurrentThread.ManagedThreadId > maxThreadId) maxThreadId = Thread.CurrentThread.ManagedThreadId;
这在我的机器上显示的是,与当前设置的另一台机器相比,ForEach 减少了 10 个线程的使用。如果您想从 ForEach 中获得更多线程,则必须摆弄 ParallelOptions 和 Scheduler。

参见

Parallel.ForEach 是否限制活动线程数?


3
投票
我想我可以回答你的问题。首先,你没有写你的系统有多少个核心。如果您运行的是双核,则只有 4 个线程可以使用

Parallel.For

,而您在 
Thread
 示例中使用 10 个线程。更多线程会更好地工作,因为您正在运行的任务(打印+短睡眠)是一个非常短的线程任务,并且与该任务相比,线程开销非常大,我几乎可以肯定,如果您在没有线程的情况下编写相同的代码它会工作得更快。

您的两种方法的工作原理几乎相同,但如果您提前创建所有线程,您可以节省很多,因为

Parallel.For

使用任务池,这会增加一些移动开销。


1
投票
关于 Threading.Parallel 的比较不太公平。您告诉自定义线程池它将需要 10 个线程。 Threading.Parallel 不知道需要多少线程,因此它会尝试在运行时进行调整,同时考虑当前 CPU 负载和其他因素。由于测试中的迭代次数足够小,您可以对这个线程数进行自适应惩罚。为 Threading.Parallel 提供相同的提示将使其运行速度更快:

int workerThreads; int completionPortThreads; ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); ThreadPool.SetMinThreads(10, completionPortThreads);
    

0
投票
这是合乎逻辑的:-)

这将是历史上第一次添加一层(或两层)代码来提高性能。当您使用便利库时,您应该付出代价。顺便说一句,你还没有发布数字。必须发布结果:-)

为了让 Parallel-s 的事情变得更加失败(或有偏见:-),请将列表转换为数组。

然后,为了让它们变得完全不公平,你可以自己分配工作,制作一系列只有 10 个项目的数组,并完全将动作喂给并行。当然,您现在正在做 Parallel-s 承诺为您做的工作,但这肯定是一个有趣的数字:-)

顺便说一句,我刚刚读了里德的博客。本题所用的分区是他所说的最简单、最幼稚的分区。

这确实是一个非常好的消除测试。您仍然需要检查零工作案例,只是为了知道它是否完全被冲洗。

© www.soinside.com 2019 - 2024. All rights reserved.