我一直在测试System.Threading.Parallel和线程的性能,我很惊讶看到Parallel需要更长的时间来完成任务而不是线程。我确定这是因为我对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();
更新
考虑锁定:我尝试了以下代码段。同样的结果,Parallel似乎完成得慢得多。
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个核心可用。
答案 0 :(得分:3)
参考Reed Copsey Jr的blog post:
Parallel.ForEach有点复杂。使用通用IEnumerable时,事先不知道处理所需的项目数,必须在运行时发现。此外,由于我们无法直接访问每个元素,因此调度程序必须枚举集合以对其进行处理。 由于IEnumerable不是线程安全的,因此它必须锁定元素,因为它枚举,为每个要处理的块创建临时集合,并安排出来。
锁定和复制可能会使Parallel.ForEach花费更长时间。此外,分区和ForEach的调度程序可能会影响并提供开销。我测试了你的代码,增加了每个任务的睡眠时间,然后结果更接近,但ForEach仍然较慢。
[编辑 - 更多研究]
我在执行循环中添加了以下内容:
if (Thread.CurrentThread.ManagedThreadId > maxThreadId)
maxThreadId = Thread.CurrentThread.ManagedThreadId;
在我的机器上显示的是,与使用当前设置的另一个相比,它使用ForEach少了10个线程。如果你想要更多的ForEach线程,你将不得不摆弄ParallelOptions和Scheduler。
请参阅Does Parallel.ForEach limits the number of active threads?
答案 1 :(得分:3)
我想我可以回答你的问题。首先,您没有编写系统拥有的核心数。如果您正在运行双核,则在Parallel.For
示例中使用10个线程时,只有4个线程可以使用Thread
。更多线程将更好地工作,因为您正在运行的任务(Printing + Short sleep)是一个非常短的线程任务,并且与任务相比,线程开销非常大,我几乎可以肯定,如果您编写相同的代码而没有线程它会更快地运作。
你的方法的工作方式几乎相同,但是如果你事先创建了所有的线程,你会节省很多,因为Parallel.For
使用了任务池,增加了一些移动开销。
答案 2 :(得分:0)
关于Threading.Parallel的比较不是很公平。你告诉你的自定义线程池,它需要10个线程。 Threading.Parallel不知道它需要多少线程,所以它试图在运行时适应考虑当前CPU负载和其他事情。由于测试中的迭代次数足够小,因此可以对此线程数进行自适应惩罚。为Threading.Parallel提供相同的提示将使其运行得更快:
int workerThreads;
int completionPortThreads;
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
ThreadPool.SetMinThreads(10, completionPortThreads);
答案 3 :(得分:0)
这是合乎逻辑的: - )
这将是历史上第一次增加一(或两)层代码来提高性能。当您使用便利库时,您应该支付价格。顺便说一句,你没有发布数字。要发布结果: - )
为了使Parallel-s更加失败(或有偏见:-),将列表转换为数组。
然后为了使它们完全不公平,自己拆分工作,制作一个只有10个项目的数组,并完全将勺子进给行动到Parallel。你当然正在完成Parallel-s承诺为你做的工作,但这肯定是一个有趣的数字: - )
顺便说一下,我刚读过里德的博客。在这个问题中使用的分区是他所说的最简单和天真的分区。 这确实是一个非常好的淘汰测试。你仍然需要检查零工作情况,只是为了知道它是否完全被冲洗。