C# 并行的性能令人失望
我正试图通过使用C# 并行的性能令人失望,c#,performance,task-parallel-library,C#,Performance,Task Parallel Library,我正试图通过使用Parallel.For来加快我的计算速度。我有一个Intel Core i7 Q840 CPU,有8个内核,但与顺序for循环相比,我只获得了4的性能比。对于,这是否与使用并行程序所能达到的效果一样好,还是可以对方法调用进行微调以提高性能 这是我的测试代码,顺序: var loops = 200; var perloop = 10000000; var sum = 0.0; for (var k = 0; k < loops; ++k) { var sumk =
Parallel.For
来加快我的计算速度。我有一个Intel Core i7 Q840 CPU,有8个内核,但与顺序for
循环相比,我只获得了4的性能比。对于,这是否与使用并行程序所能达到的效果一样好,还是可以对方法调用进行微调以提高性能
这是我的测试代码,顺序:
var loops = 200;
var perloop = 10000000;
var sum = 0.0;
for (var k = 0; k < loops; ++k)
{
var sumk = 0.0;
for (var i = 0; i < perloop; ++i) sumk += (1.0 / i) * i;
sum += sumk;
}
但是没有用。我不清楚为什么不是所有的CPU功率都分配给并行计算
我注意到有人就此提出了类似的问题,结果更令人失望。然而,这个问题还涉及第三方库中的低级并行化。我主要关心的是核心库中基本操作的并行化
更新
有人在一些评论中向我指出,我使用的CPU只有4个物理内核,如果启用了超线程,则系统可以看到8个物理内核。为此,我禁用了超线程并重新进行了基准测试
禁用“超线程”后,我的计算速度现在加快了,并行循环和(我认为是)顺序循环都加快了。for
循环期间的CPU利用率高达约45%(!!!),而并行循环期间的CPU利用率为100%
循环的计算时间为15.6秒(比启用超线程时快一倍多),并行循环的计算时间为6.2秒。
(比启用超线程时快25%)。与并行的性能比。For
现在只有2.5,运行在4个实际内核上
因此,尽管禁用了超线程,但性能比仍然大大低于预期。另一方面,有趣的是,在for
循环期间,CPU利用率如此之高?这个循环中是否也会进行某种内部并行化?Parallel.For和Parallel.ForEach将使用它认为合适的并行度,平衡设置和拆卸线程的成本以及它期望每个线程执行的工作
请注意,即使要为每个核心启动一个线程,上下文切换、问题、资源锁和其他问题也可能会阻止您实现线性可伸缩性(通常,不一定是特定代码示例)。使用全局变量可能会导致严重的同步问题,即使您不使用锁。当您为变量赋值时,每个核心必须访问系统内存中的同一位置,或者等待另一个核心完成后才能访问它。
通过使用打火机方法在操作系统级别原子地向总和添加一个值,可以避免没有锁的损坏,但仍然会由于争用而导致延迟
正确的方法是更新线程局部变量以创建部分和,并在末尾将所有部分和添加到单个全局和。有一个重载,它就是这样做的。MSDN甚至有一个例子在
int[]nums=Enumerable.Range(01000).ToArray();
长总计=0;
//使用类型参数将小计设置为long,而不是int
对于(0,nums.Length,()=>0,(j,loop,subtotal)=>
{
小计+=nums[j];
返回小计;
},
(x) =>联锁。添加(参考总数,x)
);
每个线程更新自己的小计值,并使用Interlocked.Add更新全局总计。完成后添加。我认为计算增益很低,因为您的代码“太容易”在每个迭代中处理其他任务-因为并行。对于每个迭代,只需创建新任务,所以这将需要时间在线程中为它们提供服务。我会这样做:
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
Parallel.ForEach(
Partitioner.Create(0, nums.Length),
() => 0,
(part, loopState, partSum) =>
{
for (int i = part.Item1; i < part.Item2; i++)
{
partSum += nums[i];
}
return partSum;
},
(partSum) =>
{
Interlocked.Add(ref total, partSum);
}
);
int[]nums=Enumerable.Range(01000).ToArray();
长总计=0;
并行ForEach(
创建(0,nums.Length),
() => 0,
(部分、循环状态、部分和)=>
{
对于(int i=part.Item1;i
{
联锁。添加(参考总计、部分合计);
}
);
Partitioner将为每个任务创建作业的最佳部分,使用线程执行服务任务的时间将更少。如果可以,请对该解决方案进行基准测试,并告诉我们它是否加快了速度。foreach vs parallel for each示例
for (int i = 0; i < 10; i++)
{
int[] array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 };
Stopwatch watch = new Stopwatch();
watch.Start();
//Parallel foreach
Parallel.ForEach(array, line =>
{
for (int x = 0; x < 1000000; x++)
{
}
});
watch.Stop();
Console.WriteLine("Parallel.ForEach {0}", watch.Elapsed.Milliseconds);
watch = new Stopwatch();
//foreach
watch.Start();
foreach (int item in array)
{
for (int z = 0; z < 10000000; z++)
{
}
}
watch.Stop();
Console.WriteLine("ForEach {0}", watch.Elapsed.Milliseconds);
Console.WriteLine("####");
}
Console.ReadKey();
for(int i=0;i<10;i++)
{
int[]数组=新的int[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29};
秒表=新秒表();
watch.Start();
//平行foreach
Parallel.ForEach(数组,行=>
{
对于(int x=0;x<1000000;x++)
{
}
});
看,停;
WriteLine(“Parallel.ForEach{0}”,watch.appeased.millizes);
手表=新秒表();
//弗雷奇
watch.Start();
foreach(数组中的int项)
{
对于(int z=0;z<10000000;z++)
{
}
}
看,停;
WriteLine(“ForEach{0}”,watch.appeased.millizes);
Console.WriteLine(“######”);
}
Console.ReadKey();
我的CPU
英特尔®核心™ i7-620M处理器(4M高速缓存,2.66GHz)并行循环不需要对求和变量进行锁定吗?@SteveB我相信你是对的。但是,使用显式锁不会提高性能,对吗?:-)你确定你有8个核吗?例如,根据i7-840QM,由于具有多线程功能,操作系统可以将4个内核视为8个。超线程将愚弄操作系统,使其知道您有8个内核。你可以在bios中关闭它。超线程技术在许多程序的日常工作中运行良好。正如@Magnus所说,它将“fo
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
// Use type parameter to make subtotal a long, not an int
Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) =>
{
subtotal += nums[j];
return subtotal;
},
(x) => Interlocked.Add(ref total, x)
);
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
Parallel.ForEach(
Partitioner.Create(0, nums.Length),
() => 0,
(part, loopState, partSum) =>
{
for (int i = part.Item1; i < part.Item2; i++)
{
partSum += nums[i];
}
return partSum;
},
(partSum) =>
{
Interlocked.Add(ref total, partSum);
}
);
for (int i = 0; i < 10; i++)
{
int[] array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 };
Stopwatch watch = new Stopwatch();
watch.Start();
//Parallel foreach
Parallel.ForEach(array, line =>
{
for (int x = 0; x < 1000000; x++)
{
}
});
watch.Stop();
Console.WriteLine("Parallel.ForEach {0}", watch.Elapsed.Milliseconds);
watch = new Stopwatch();
//foreach
watch.Start();
foreach (int item in array)
{
for (int z = 0; z < 10000000; z++)
{
}
}
watch.Stop();
Console.WriteLine("ForEach {0}", watch.Elapsed.Milliseconds);
Console.WriteLine("####");
}
Console.ReadKey();