我正在构建用于评估许多可能解决方案的软件,并试图引入并行处理以加快计算速度。我的第一个尝试是建立一个数据表,每一行都是要评估的解决方案,但是建立数据表需要花费很多时间,而且当可能的解决方案数以百万计时,我遇到了内存问题。
需要这些解决方案的问题的结构如下: 有x个事件的范围日期,必须按顺序进行。评估的解决方案如下所示,每种解决方案都是一行,事件是列,而天数是值。
给出3天(0到2天)和3个事件:
0 0 0
0 0 1
0 0 2
0 1 1
0 1 2
0 2 2
1 1 1
1 1 2
1 2 2
2 2 2
我的新计划是使用递归并在进行过程中评估解决方案,而不是构建解决方案集然后进行评估。
for(int day = 0; day < maxdays; day++)
{
List<int> mydays = new List<int>();
mydays.Add(day);
EvalEvent(0,day,mydays);
}
private void EvalEvent(int eventnum,
int day, List<int> mydays)
{
Parallel.For(day,maxdays, day2 =>
// events must be on same day or after previous events
{
List<int> mydays2 = new List<int>();
for(int a = 0; a <mydays.Count;a++)
{
mydays2.Add(mydays[a]);
}
mydays2.Add(day2);
if(eventnum< eventcount - 1) // proceed to next event
{
EvalEvent(eventnum+1, day2,mydays2);
}
else
{
EvalSolution(mydays2);
}
});
}
我的问题是,这是否实际上是对并行处理的有效利用,还是会产生太多线程并减慢速度?应该只在eventnum的最后一个值还是最后几个值上执行并行循环,还是有更好的方法来解决该问题?
private int daterange;
private int events;
private void ScheduleIt()
{
daterange = 10;
events = 6;
CreateSolutions();
int best = GetBest();
}
private DataTable Options();
private bool CreateSolutions()
{
Options= new DataTable();
Options.Columns.Add();
for (int day1=0;day1<=daterange ;day1++)
{
Options.Rows.Add(day1);
}
for (int event =1; event<events; event++)
{
Options.Columns.Add();
foreach(DataRow dr in Options.Rows)
{dr[Options.Columns.Count-1] = dr[Options.Columns.Count-2] ;}
int rows = Options.Rows.Count;
for (int day1=1;day1<=daterange ;day1++)
{
for(int i = 0; i <rows; i++)
{
if(day1 > Convert.ToInt32(Options.Rows[i][Options.Columns.Count-2]))
{
try{
Options.Rows.Add();
for (int col=0;col<Options.Columns.Count-1;col++)
{
Options.Rows[Options.Rows.Count-1][col] =Options.Rows[i][col];
}
Options.Rows[Options.Rows.Count-1][Options.Columns.Count-1] = day1;
}
catch(Exception ex)
{
return false;
}
}
}
}
}
return true;
}
private intGetBest()
{
int bestopt = 0;
double bestscore =999999999;
Parallel.For( 0, Options.Rows.Count,opt =>
{
double score = 0;
for(int i = 0; i <Options.Columns.Count;i++)
{score += Options.Rows[opt][i]}// just a stand in calc for a score
if (score < bestscore)
{bestscore = score;
bestopt = opt;
}
});
return bestopt;
答案 0 :(得分:1)
即使完成没有错误,也无法显着加快解决方案的速度。
似乎每个递归级别都尝试开始多个(例如最多说“ k”)下一级别的调用,让我们称之为“ n”级别。这实质上意味着代码是O(k ^ n),它增长非常快。这样的O(k ^ n)解决方案的非算法加速本质上是没有用的(除非k和n都非常小)。特别是,并行运行代码只会给您带来恒定的速度提升(CPU支持的线程数量大约),而这根本不会改变复杂性。
实际上,对新线程的请求创建成指数级增长,仅管理线程本身可能会引起更多问题。
除了不能显着提高性能之外,并行代码更难编写,因为它需要适当的同步或切割器数据分区-在您的情况下似乎都不存在。
答案 1 :(得分:0)
当工作量很大且平衡时,并行化效果最佳。理想情况下,您希望将工作划分为与计算机逻辑处理器一样多的独立分区,以确保所有分区的大小大致相同。这样,所有可用的处理器将在大约相同的持续时间内以最大的效率工作,并且您将在最短的时间内得到结果。
当然,您应该从一个无错误的串行实现开始,然后考虑对工作进行分区的方法。最简单的方法通常不是最佳方法。例如,一个简单的方法是将您的工作转换为LINQ查询,然后将其与AsParallel()
并行化(使其成为PLINQ)。这通常会导致分区过于精细,从而导致过多的开销。如果您找不到改善的方法,则可以采用Parallel.For
或Parallel.ForEach
的方法,它们要复杂一些。
LINQ实现可能应该从创建一个迭代器开始,该迭代器将生成您所有的工作单元(事件或解决方案,对我来说不是很清楚)。
public static IEnumerable<Solution> GetAllSolutions()
{
for (int day = 0; day < 3; day++)
{
for (int ev = 0; ev < 3; ev++)
{
yield return new Solution(); // ???
}
}
}
如果您创建了具体的类来表示您要处理的实体,那肯定会有所帮助。