C# 使用单个并行for循环获取Min、Max和
我试图从一个大数组中获取最小值、最大值和总和(平均值)。我想用parallel.for替换我的常规for循环C# 使用单个并行for循环获取Min、Max和,c#,parallel-processing,task-parallel-library,C#,Parallel Processing,Task Parallel Library,我试图从一个大数组中获取最小值、最大值和总和(平均值)。我想用parallel.for替换我的常规for循环 UInt16 tempMin = (UInt16)(Math.Pow(2,mfvm.cameras[openCamIndex].bitDepth) - 1); UInt16 tempMax = 0; UInt64 tempSum = 0; for (int i = 0; i < acquisition.frameDataShorts.Length; i++) { if (
UInt16 tempMin = (UInt16)(Math.Pow(2,mfvm.cameras[openCamIndex].bitDepth) - 1);
UInt16 tempMax = 0;
UInt64 tempSum = 0;
for (int i = 0; i < acquisition.frameDataShorts.Length; i++)
{
if (acquisition.frameDataShorts[i] < tempMin)
tempMin = acquisition.frameDataShorts[i];
if (acquisition.frameDataShorts[i] > tempMax)
tempMax = acquisition.frameDataShorts[i];
tempSum += acquisition.frameDataShorts[i];
}
UInt16 tempMin=(UInt16)(Math.Pow(2,mfvm.cameras[openCamIndex].bitDepth)-1);
UInt16 tempMax=0;
UInt64 tempSum=0;
for(int i=0;itempMax)
tempMax=acquisition.frameDataShorts[i];
tempSum+=acquisition.frameDataShorts[i];
}
我知道如何使用自己切割阵列的任务来解决这个问题。然而,我很想学习如何使用parallel.for。因为据我所知,它应该能够做到非常优雅
我找到了计算和的方法,但是我不知道如何将它扩展到在一篇文章中完成所有三件事(最小值、最大值和和和)
结果:
好的,我尝试了PLINQ解决方案,我看到了一些严重的改进。
我的i7(2x4个内核)上有3个过程(最小、最大、和),比顺序aproach快4倍。然而,我在Xeon(2x8内核)上尝试了相同的代码,结果完全不同。并行(同样是3次)的速度实际上是顺序aproach的两倍(比我的i7快5倍)
最后,我自己用Task Factory分离了阵列,在所有计算机上的结果都稍好一些。我不认为并行。for很适合这里,但请尝试以下方法:
public class MyArrayHandler {
public async Task GetMinMaxSum() {
var myArray = Enumerable.Range(0, 1000);
var maxTask = Task.Run(() => myArray.Max());
var minTask = Task.Run(() => myArray.Min());
var sumTask = Task.Run(() => myArray.Sum());
var results = await Task.WhenAll(maxTask,
minTask,
sumTask);
var max = results[0];
var min = results[1];
var sum = results[2];
}
}
编辑
只是为了好玩,由于对性能的评论,我做了一些测量。还有,我发现了这个
@10000000个值
GetMinMax:218ms
GetMinMaxAsync:308ms
public class MinMaxSumTests {
[Test]
public async Task GetMinMaxSumAsync() {
var myArray = Enumerable.Range(0, 10000000).Select(x => (long)x).ToArray();
var sw = new Stopwatch();
sw.Start();
var maxTask = Task.Run(() => myArray.Max());
var minTask = Task.Run(() => myArray.Min());
var sumTask = Task.Run(() => myArray.Sum());
var results = await Task.WhenAll(maxTask,
minTask,
sumTask);
var max = results[0];
var min = results[1];
var sum = results[2];
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
[Test]
public void GetMinMaxSum() {
var myArray = Enumerable.Range(0, 10000000).Select(x => (long)x).ToArray();
var sw = new Stopwatch();
sw.Start();
long tempMin = 0;
long tempMax = 0;
long tempSum = 0;
for (int i = 0; i < myArray.Length; i++) {
if (myArray[i] < tempMin)
tempMin = myArray[i];
if (myArray[i] > tempMax)
tempMax = myArray[i];
tempSum += myArray[i];
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
公共类MinMaxSumTests{
[测试]
公共异步任务GetMinMaxSumAsync(){
var myArray=Enumerable.Range(010000).Select(x=>(long)x.ToArray();
var sw=新秒表();
sw.Start();
var maxTask=Task.Run(()=>myArray.Max());
var minTask=Task.Run(()=>myArray.Min());
var sumTask=Task.Run(()=>myArray.Sum());
var结果=等待任务.WhenAll(maxTask,
薄荷糖,
sumTask);
var max=结果[0];
var min=结果[1];
var总和=结果[2];
sw.Stop();
控制台写入线(软件延迟毫秒);
}
[测试]
public void GetMinMaxSum(){
var myArray=Enumerable.Range(010000).Select(x=>(long)x.ToArray();
var sw=新秒表();
sw.Start();
长tempMin=0;
长tempMax=0;
长tempSum=0;
for(int i=0;itempMax)
tempMax=myArray[i];
tempSum+=myArray[i];
}
sw.Stop();
控制台写入线(软件延迟毫秒);
}
}
我假设这里的主要问题是每次迭代都必须记住三个不同的变量。为此,您可以使用元组
:
var lockObject = new object();
var arr = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
var min = arr[0];
var max = arr[0];
Parallel.For(0, arr.Length,
() => new Tuple<long, int, int>(0, arr[0], arr[0]),
(i, loop, temp) => new Tuple<long, int, int>(temp.Item1 + arr[i], Math.Min(temp.Item2, arr[i]),
Math.Max(temp.Item3, arr[i])),
x =>
{
lock (lockObject)
{
total += x.Item1;
min = Math.Min(min, x.Item2);
max = Math.Max(max, x.Item3);
}
}
);
var lockObject=新对象();
var arr=Enumerable.Range(0,1000000).ToArray();
长总计=0;
var min=arr[0];
var max=arr[0];
对于(0,棱长,
()=>新元组(0,arr[0],arr[0]),
(i,loop,temp)=>新元组(temp.Item1+arr[i],Math.Min(temp.Item2,arr[i]),
数学最大值(临时项目3,arr[i]),
x=>
{
锁定(锁定对象)
{
总计+=x.1项;
min=数学最小值(min,x.Item2);
max=数学最大值(max,x.Item3);
}
}
);
不过,我必须警告您,这个实现(在我的机器上)比您在问题中演示的简单for-loop方法慢10倍左右,因此请小心操作。不要重新发明轮子,
Min
,Max
Sum
,类似的操作是聚合。从.NET v3.5开始,您就有了方便的扩展方法版本,这些扩展方法已经为您提供了解决方案:
using System.Linq;
var sequence = Enumerable.Range(0, 10).Select(s => (uint)s).ToList();
Console.WriteLine(sequence.Sum(s => (double)s));
Console.WriteLine(sequence.Max());
Console.WriteLine(sequence.Min());
尽管它们被声明为IEnumerable
的扩展,但它们对IList
和Array
类型有一些内部改进,因此您应该衡量代码在该类型和IEnumerable
上的工作方式
在您的例子中,这是不够的,因为您显然不想多次迭代另一个数组,所以这里有一个魔术:(又称并行LINQ)。只需添加一个方法即可并行聚合数组:
var sequence = Enumerable.Range(0, 10000000).Select(s => (uint)s).AsParallel();
Console.WriteLine(sequence.Sum(s => (double)s));
Console.WriteLine(sequence.Max());
Console.WriteLine(sequence.Min());
此选项为同步项增加了一些开销,但它的伸缩性很好,为小型和大型枚举提供了类似的时间。从MSDN:
每当您需要将并行聚合模式应用于.NET应用程序时,PLINQ
通常是推荐的方法。它的声明性使得它比其他方法更不容易出错,而且它在多核计算机上的性能与其他方法相当
使用PLINQ
实现并行聚合不需要在代码中添加锁。相反,所有同步都在内部进行,在PLINQ
中
但是,如果您仍然希望调查不同类型操作的性能,可以使用Parallel.for
和Parallel.foreach
方法通过某种聚合方法重载,如下所示:
double[] sequence = ...
object lockObject = new object();
double sum = 0.0d;
Parallel.ForEach(
// The values to be aggregated
sequence,
// The local initial partial result
() => 0.0d,
// The loop body
(x, loopState, partialResult) =>
{
return Normalize(x) + partialResult;
},
// The final step of each local context
(localPartialSum) =>
{
// Enforce serial access to single, shared result
lock (lockObject)
{
sum += localPartialSum;
}
}
);
return sum;
如果您的数据需要额外的分区,您可以使用分区器
进行以下方法:
var rangePartitioner = Partitioner.Create(0, sequence.Length);
Parallel.ForEach(
// The input intervals
rangePartitioner,
// same code here);
另外,Aggregate
方法可用于PLINQ
,具有一些合并逻辑(请再次参阅MSDN中的插图): 有用链接:
- 方法
- 方法
- 方法