我的问题是如何最好地构建我的(C ++)代码以支持并行化耗时的计算。有问题的伪代码具有以下结构:
for a_1(y_1) in A_1
for a_2(y_2) in A_2(a_1)
...
for a_n(y_n) in A_n(a_1, ..., a_{n-1})
y_n = f(a_1, ..., a_n)
y_{n-1} = g_n(Y_n)
...
y_1 = g_2(Y_2)
.
粗略地说,每个循环遍历集合A_i
中的元素,其连续元素依赖于先前迭代的反馈y_i
。换句话说,要确定下一个a_i
,我们必须完成当前a_i
的所有计算。此外,内部集合取决于外部迭代。以递归形式编写:
Iterate(A_i, a_1, ..., a_{i-1}):
for a_i(h_i) in A_i
Y_i += Iterate(A_{i+1}, a_1, ..., a_i)
return g(Y_i)
Iterate(any, a_1, ..., a_n):
return f(a_1, ..., a_n)
Iterate(A_1)
假设f(...)是耗时的计算,并且反馈函数g(...)是简单的(快速)。现在,如果所有集合A_i
都是“大”,那么问题就是令人尴尬的可并行化。目前,我有一个线程池,只是将最内层循环的计算放入池中。问题是,最内层循环通常是对单例的迭代,因此线程池中只有一个正在运行的线程。我曾考虑使用期货将价值返回到外部循环,但这需要期货期货等,而且它会变得非常混乱(我认为)。
我意识到我上面列出的结构非常复杂,所以我也感兴趣的是一些简化的案例:
a_i(h_i) = a_i
;独立于h_i A_i(a_1, ..., a_{i-1}) = A_i
;独立于a_1,... a_ {i-1} g_i = 0
;独立于H_ {i + 1} 现在,实际上,n <= 3,并且项目1适用于所有外部循环,而项目2-4都保持不变,因此针对该情况的特定解决方案就足够了。但由于我在这里提出这个问题,我很想知道如果可能的话,如何处理更多一般性问题的额外复杂性。
修改
清理第一个伪代码块以使其与另一个伪代码块保持一致。 由于人们无法理解我的数学符号,这里有一个更具体的简单例子:
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
double f(double a1, int a2, double a3){ // Very slow function
cout << a1 << ", " << a2 << ", " << a3 << endl;
return pow(a1*a3, a2) + a1 + a2 + a3; // just some contrived example
}
int g2(const vector<double> &Y3){ // average-ish
double sum = 0;
for(int i = 0; i < Y3.size(); ++i){ sum += Y3[i]; }
return int(sum / (Y3.size() + 1));
}
double g1(const vector<int> &Y2){ // return 1/(min(Y2)+1.0)
int imin = 0; int minval = 0;
for(int i = 1; i < Y2.size(); ++i){
if(Y2[i] < minval){
imin = i;
minval = Y2[imin];
}
}
return 1.0/double(minval+1.0);
}
int main(){
for(double a1 = 0.0, h1 = 10.0; a1 < 1.0; a1 += h1){ // for a1 in A1
vector<int> Y2;
for(int a2 = 2, h2 = 1; a2 <= (int)(5*a1+10); a2 += h2){ // for a2 in A2(a1)
vector<double> Y3;
for(double a3 = 6.0, h3 = 1.0; a3 >= (a1+a2); a3 -= 0.5*h3){ // for a3 in A2(a1, a2)
h3 = f(a1, a2, a3);
Y3.push_back(h3);
}
h2 = g2(Y3);
Y2.push_back(h2);
}
h1 = g1(Y2);
}
return 0;
}
我随机选择了值,结果f
仅被评估了3次。请注意,上面的代码不可并行化。 假设可以查询循环的增量是否依赖于更高的循环。
我应该澄清我所追求的东西。当我最初说结构时,我或许应该说并行化方法或类似的东西。例如,我第一次尝试并行化是将最内部调用f
抛出到一个线程池中,并在最内层循环的末尾加入。如上所述,当最内层循环仅在一个元素上迭代时,这不起作用。这并不需要显着重构现有代码,如果可能的话我想避免使用它。
答案 0 :(得分:2)
您可以尝试以map-reduce问题(http://en.wikipedia.org/wiki/MapReduce)的形式表达您的问题,使每个级别的嵌套都成为一个map-reduce作业。 for循环将转换为映射,g_i调用简化步骤。
您可以尝试使您的伪语言更清晰......也许表达为n = 3或n = 4的python程序?你的“for”是一个普通的for
循环吗?如果是这样,我真的不明白第一对括号。
我不确定您的问题是否可以按照声明的形式进行并行化。如果你说循环的变量取决于之前的迭代,那么它看起来更像是一个顺序问题。
答案 1 :(得分:0)
说实话,乍一看你的记谱很难(至少对我而言)。也许你可能更明确或可能使用C或C ++代码。你的并行化方法是什么(pthreads,openmp等)?我怀疑一个问题是你可以改善你的负载平衡。例如,您可能不希望将工作分配给卡交易方式中的线程。
答案 2 :(得分:0)
如果可能的话,加速这类深层嵌套调用的最佳方法就是没有深层嵌套的调用。
您通常可以重新组织数据或数据中的链接,以获得可能为您提供循环级别的引用,或者您有时可以找到一种方法来依次排列循环,存储中间信息。有时它甚至会创建一个不同的对象结构。
我并不是说这总是有效,但即使删除一个级别,也会比你可能尝试的其他任何事情都显着减少。
如果我能理解你的伪代码,我可以尝试一下,但我猜你已经抽出了大部分结构,无论如何都需要有洞察力的设计。