我的代码使用简单的For循环很有效,但我正在努力加快速度。我正在尝试调整代码以使用多个内核并登陆Parallel For。
在高级别,我正在从CalcRoutine收集数千个帐户的结果,并将结果存储在包含6个元素的数组中。然后我重新运行这个过程1000次。每个6元素数组中元素的顺序很重要,但这6个元素数组的最后1,000次迭代的顺序并不重要。当我使用For循环运行代码时,我得到一个6,000个元素长的列表。但是,当我尝试Parallel For版本时,我得到了接近600的东西。我已经确认“返回localResults”行被调用了1000次,但由于某种原因,并非所有6个元素数组都被添加到列表中TotalResults 。任何关于为什么这不起作用的见解将不胜感激。
object locker = new object();
Parallel.For(0, iScenarios, () => new double[6], (int k, ParallelLoopState state, double[] localResults) =>
{
List<double> CalcResults = new List<double>();
for (int n = iStart; n < iEnd; n++)
{
CalcResults.AddRange(CalcRoutine(n, k));
}
localResults = this.SumOfResults(CalcResults);
return localResults;
},
(double[] localResults) =>
{
lock (locker)
{
TotalResults.AddRange(localResults);
}
});
编辑:这是“非平行”版本:
for (int k = 0; k < iScenarios; k++)
{
CalcResults.Clear();
for (int n = iStart; n < iEnd; n++)
{
CalcResults.AddRange(CalcRoutine(n, k));
}
TotalResults.AddRange(SumOfResults(CalcResults));
}
1场景的输出是6个双打的列表,2个场景是12个双打的列表,... n场景6n加倍。
同样对于其中一个问题,我检查了“TotalResults.AddRange ...”被调用的次数,并且不是完整的1000次。为什么每次都不会这个叫?使用锁定,每个线程不应该等待此部分可用吗?
答案 0 :(得分:2)
这些初始状态将传递给每个任务的第一个 body 调用。然后,每个后续的 body 调用都会返回一个可能已修改的状态值,该值将传递给下一个 body 调用。最后,每个任务的最后一个 body 调用返回一个传递给 localFinally 委托的状态值
但是你的 body 委托忽略了localResults
的传入值,该值返回了此任务中的上一次迭代。将循环状态作为数组使得编写正确的版本变得棘手。这会有效,但看起来很乱:
//EDIT - Create an array of length 0 here V for input to first iteration
Parallel.For(0, iScenarios, () => new double[0],
(int k, ParallelLoopState state, double[] localResults) =>
{
List<double> CalcResults = new List<double>();
for (int n = iStart; n < iEnd; n++)
{
CalcResults.AddRange(CalcRoutine(n, k));
}
localResults = localResults.Concat(
this.SumOfResults(CalcResults)
).ToArray();
return localResults;
},
(double[] localResults) =>
{
lock (locker)
{
TotalResults.AddRange(localResults);
}
});
(假设Linq的可枚举扩展名在范围内,适用于Concat
)
我建议使用不同的数据结构(例如List<double>
而不是double[]
)来表示更自然地允许添加更多元素的状态 - 但这意味着要更改{{ 1}}你没有表现出来。或者只是让它更加抽象:
SumOfResults
(如果它按照你似乎已经假设的方式工作,为什么他们会提供两个单独的委托,如果它完成了,从 body 返回,就是立即调用 localFinally 带有返回值?)
答案 1 :(得分:0)
试试这个:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
var iScenarios = 6;
var iStart = 0;
var iEnd = 1000;
var totalResults = new List<double>();
Parallel.For(0, iScenarios, k => {
List<double> calcResults = new List<double>();
for (int n = iStart; n < iEnd; n++)
calcResults.AddRange(CalcRoutine(n, k));
lock (totalResults)
{
totalResults.AddRange(calcResults);
}
});
}
static IEnumerable<double> CalcRoutine(int a, int b)
{
yield return 0;
}
static double[] SumOfResults(IEnumerable<double> source)
{
return source.ToArray();
}
}