我有一个非常大的二叉树,我想在其上进行某些昂贵的计算。我想使用openmp及其任务编译来并行化这些方法。
作为测试,我已经并行化了freeTree
函数,并创建了一个大型示例测试树。为了防止产生许多任务,我将任务创建限制在树的前两个级别。所以实际上只创建了4个任务。
以下是最小的工作示例:
#include <chrono>
#include <iostream>
class Node {
public:
int data;
Node* l;
Node* r;
Node(Node* left, Node* right) : l(left), r(right) {}
};
Node* createRandomTree(int depth) {
if (depth == 0)
return new Node(NULL, NULL);
return new Node(createRandomTree(depth - 1), createRandomTree(depth - 1));
}
void freeTree(Node* tree) {
if (tree == NULL) return;
freeTree(tree->l);
freeTree(tree->r);
delete tree;
}
void freeTreePar(Node* tree, int n = 0) {
if (tree == NULL) return;
Node *l = tree->l, *r = tree->r;
if (n < 2) {
#pragma omp task
freeTreePar(l, n + 1);
#pragma omp task
freeTreePar(r, n + 1);
} else {
freeTree(tree->l);
freeTree(tree->r);
}
// taskwait is not necessary
delete tree;
}
int main(int argc, char const *argv[])
{
std::chrono::time_point<std::chrono::system_clock> start, end;
Node* tree = createRandomTree(22);
start = std::chrono::system_clock::now();
#pragma omp parallel shared(tree)
{
#pragma omp single nowait
freeTreePar(tree);
}
end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::cout << "finished computation at " << std::ctime(&end_time)
<< "elapsed time: " << elapsed_seconds.count() << "s\n";
return 0;
}
当我运行此代码时,释放树需要大约 0.38 秒。但是,如果我直接拨打freeTree(root)
,则只需 0.2 秒。因此,即使树非常大(2 ^ 22个元素),即使在这个特定的测试用例中,任务大小相同,但使用并行方法无法提高性能。
我做错了吗?你知道如何改进这段代码吗?谢谢!
答案 0 :(得分:3)
某些任务实际上不可并行化,因为某些资源一次只能由一个线程访问(线程安全)。这就是动态记忆的情况。
malloc / free现在主要是线程安全的。
=&GT;将在每个malloc / free周围执行锁定。
所以,你不能轻易改进这种代码。