我目前正在实现一个动态规划算法来解决背包问题。因此,我的代码有两个 for 循环,一个外部循环和一个内部循环。
从逻辑上讲,我可以并行化内部 for 循环,因为其中的计算是相互独立的。由于存在依赖关系,无法并行化外部 for 循环。
所以这是我的第一种方法:
for(int i=1; i < itemRows; i++){
int itemsIndex = i-1;
int itemWeight = integerItems[itemsIndex].weight;
int itemWorth = integerItems[itemsIndex].worth;
#pragma omp parallel for if(weightColumns > THRESHOLD)
for(int c=1; c < weightColumns; c++){
if(c < itemWeight){
table[i][c] = table[i-1][c];
}else{
int worthOfNotUsingItem = table[i-1][c];
int worthOfUsingItem = itemWorth + table[i-1][c-itemWeight];
table[i][c] = worthOfNotUsingItem < worthOfUsingItem ? worthOfUsingItem : worthOfNotUsingItem;
}
}
}
代码运行良好,算法正确解决了问题。 然后我开始考虑优化这个过程,因为我不确定OpenMP的线程管理是如何工作的。我希望防止在每次迭代中不必要地初始化线程,因此我在外部循环周围放置了一个外部并行块。
第二种方法:
#pragma omp parallel if(weightColumns > THRESHOLD)
{
for(int i=1; i < itemRows; i++){
int itemsIndex = i-1;
int itemWeight = integerItems[itemsIndex].weight;
int itemWorth = integerItems[itemsIndex].worth;
#pragma omp for
for(int c=1; c < weightColumns; c++){
if(c < itemWeight){
table[i][c] = table[i-1][c];
}else{
int worthOfNotUsingItem = table[i-1][c];
int worthOfUsingItem = itemWorth + table[i-1][c-itemWeight];
table[i][c] = worthOfNotUsingItem < worthOfUsingItem ? worthOfUsingItem : worthOfNotUsingItem;
}
}
}
}
这种方法有一个不想要的副作用:并行块内的所有内容都会执行n次,其中n是可用核心数。我已经尝试使用pragma single
和critical
来强制外层for循环在一个线程中执行,但这样我就无法通过多个线程计算内部循环,除非我打开一个新的并行块(但那样速度并没有提升)。但不要紧,因为好的消息是:这不影响结果。问题仍然被正确解决。
现在奇怪的事情是:第二种方法比第一种方法更快!
这怎么可能?我的意思是,虽然外部for循环在n次(并行)中计算,内部for循环在n个核心中分别分配n次,但它比第一种方法更快,第一种方法只计算了外部循环一次,并均匀分配内部for循环的工作负载。
起初我想:“嗯,这可能是由于线程管理”,但后来我读到OpenMP池化实例化的线程,这与我的假设相违背。然后我禁用编译器优化(编译器标志-O0)以检查是否与其有关。但是这并没有影响测量。
请问有人能否对此提供更多的见解?
解决包含7500个项目且最大容量为45000的背包问题所需的测量时间(创建一个7500x45000的矩阵,这远远超过了代码中使用的THRESHOLD变量):
- 方法1:约0.88秒
- 方法2:约0.52秒
预先感谢您的帮助,
phineliner
编辑:
更复杂问题的测量: 将2500个项目添加到问题中(从7500到10000)(目前由于内存原因无法处理更复杂的问题)。
- 方法1:约1.19秒
- 方法2:约0.71秒
编辑2: 我对编译器优化产生了误解。这不会影响测量。至少我不能再次验证我之前测量到的差异。我根据此编辑了问题文本。
#pragma omp for nowait
会发生什么? - Z boson