C++ openmp嵌套循环处理性能 请考虑以下代码: void process(int N, int K, const vector<int>& data) { #pragma omp parallel for for(int i = 0; i < data.size(); ++i) { //perform some processing based on data, N and K } } void solve(int N, int K, const vector<int>& data) { for(int i = 0; i < N; ++i) { for(int j = 0; j < K; ++j) { process(N, K, data); } } }
正如您在被动模式中看到的,当N*K相当大时,时间要大得多 或者其他解决这个问题的方法 当将计算分配到线程时,您希望拥有尽可能大的数据块和尽可能少的同步。在您的示例中,您应该将最外层的循环并行化。在您的示例中,不清楚C++ openmp嵌套循环处理性能 请考虑以下代码: void process(int N, int K, const vector<int>& data) { #pragma omp parallel for for(int i = 0; i < data.size(); ++i) { //perform some processing based on data, N and K } } void solve(int N, int K, const vector<int>& data) { for(int i = 0; i < N; ++i) { for(int j = 0; j < K; ++j) { process(N, K, data); } } },c++,performance,openmp,C++,Performance,Openmp,正如您在被动模式中看到的,当N*K相当大时,时间要大得多 或者其他解决这个问题的方法 当将计算分配到线程时,您希望拥有尽可能大的数据块和尽可能少的同步。在您的示例中,您应该将最外层的循环并行化。在您的示例中,不清楚过程是否修改数据。它作为非常量传递,但假设它未被修改,我希望它的性能会更好: void solve(int N, int K, vector<int>& data) { #pragma omp parallel for for(int i = 0;
过程是否修改数据。它作为非常量传递,但假设它未被修改,我希望它的性能会更好:
void solve(int N, int K, vector<int>& data)
{
#pragma omp parallel for
for(int i = 0; i < N; ++i)
{
for(int j = 0; j < K; ++j)
{
process(N, K, data);
}
} // <-- threads have to wait here until all are finished
}
void solve(int N、int K、向量和数据)
{
#pragma-omp并行
对于(int i=0;i }//基于您的MCVE,我做了一些测试,我相信您的代码编写方式存在一些问题
您正在使用std::atomic
作为累积结果的计数器的类型,并将其增加(结果是以原子方式增加)。虽然从功能角度来看这是正确的,但这不是一种非常有效的方法。将计数器更改为简单的int
,并将其声明为减少(+:cnt)并行
区域中的
要好得多
您正在N
x
K
循环中创建parallel
区域。向上移动parallel
指令可能会有所帮助。然后,您以前的#pragma omp parallel for
指令将成为孤立的#pragma omp for
指令
因此,我对这些想法进行了一些实验,下面是我现在得到的(使用4个线程,因为我的机器上有4个内核):
-您的被动策略版本:
Large N*K
Time: 74.4741 ms Threads: 4 N: 100 K: 100
Time: 40.2336 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.151747 ms Threads: 4 N: 1 K: 1
Time: 0.395791 ms Threads: 1 N: 1 K: 1
Large N*K
Time: 35.1184 ms Threads: 4 N: 100 K: 100
Time: 7.932 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.040216 ms Threads: 4 N: 1 K: 1
Time: 0.082633 ms Threads: 1 N: 1 K: 1
Large N*K
Time: 5.30402 ms Threads: 4 N: 100 K: 100
Time: 9.57645 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.028136 ms Threads: 4 N: 1 K: 1
Time: 0.094375 ms Threads: 1 N: 1 K: 1
-我的被动策略版本:
Large N*K
Time: 74.4741 ms Threads: 4 N: 100 K: 100
Time: 40.2336 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.151747 ms Threads: 4 N: 1 K: 1
Time: 0.395791 ms Threads: 1 N: 1 K: 1
Large N*K
Time: 35.1184 ms Threads: 4 N: 100 K: 100
Time: 7.932 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.040216 ms Threads: 4 N: 1 K: 1
Time: 0.082633 ms Threads: 1 N: 1 K: 1
Large N*K
Time: 5.30402 ms Threads: 4 N: 100 K: 100
Time: 9.57645 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.028136 ms Threads: 4 N: 1 K: 1
Time: 0.094375 ms Threads: 1 N: 1 K: 1
-您的版本具有活动策略
Large N*K
Time: 16.3105 ms Threads: 4 N: 100 K: 100
Time: 44.4862 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.110355 ms Threads: 4 N: 1 K: 1
Time: 0.427118 ms Threads: 1 N: 1 K: 1
-我的主动策略版本:
Large N*K
Time: 74.4741 ms Threads: 4 N: 100 K: 100
Time: 40.2336 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.151747 ms Threads: 4 N: 1 K: 1
Time: 0.395791 ms Threads: 1 N: 1 K: 1
Large N*K
Time: 35.1184 ms Threads: 4 N: 100 K: 100
Time: 7.932 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.040216 ms Threads: 4 N: 1 K: 1
Time: 0.082633 ms Threads: 1 N: 1 K: 1
Large N*K
Time: 5.30402 ms Threads: 4 N: 100 K: 100
Time: 9.57645 ms Threads: 1 N: 100 K: 100
Small N*K
Time: 0.028136 ms Threads: 4 N: 1 K: 1
Time: 0.094375 ms Threads: 1 N: 1 K: 1
由此我可以说:
删除std:atomic
和使用还原(+)
确实会产生重大影响,应继续进行
如果等待策略必须是被动的,那么使用多线程路由没有任何意义,因为在该配置中,单线程版本总是比多线程版本快
为了便于记录,以下是修改后的零件的外观:
int cnt;
void process(int a, int b, std::vector<int>& d)
{
#pragma omp for reduction(+:cnt)
for (int i = 0; i < d.size(); ++i)
{
//sample operation
if (d[i] > a + b)
++cnt;
}
}
void solve(int N, int K, std::vector<int>& d)
{
#pragma omp parallel
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < K; ++j)
{
process(i, j, d);
}
}
}
int-cnt;
无效流程(int a、int b、std::vector&d)
{
#用于还原的pragma omp(+:cnt)
对于(int i=0;ia+b)
++碳纳米管;
}
}
void solve(int N,int K,std::vector&d)
{
#pragma-omp并行
对于(int i=0;i
谢谢,我已将数据标记为常量。我同意您的说法,但在这种情况下,N和K的大小通常为1(或小于线程数)。我不希望在“进程”中丢失omp优化在这种情况下起作用。@AdamF如果你得到答案后不改变你的问题,我会更喜欢。现在我的答案已经过时了,必须修正它。无论如何,然后制作两个不同版本的solve
,调用不同版本的process
,一个是并行solve
,一个是顺序process
,另一个是e反过来,然后根据大小选择一个或另一个。这将是一个有趣的练习,看看转折点在哪里。我有点嫉妒;)在这种情况下,我可以完全删除N和K上的循环,并在过程中迭代0..data.size()*N*K.但这会让代码变得丑陋,维护起来更困难,然后我还需要正确计算索引。@AdamF你写的东西在你发布的代码中并不明显。如果你能将循环展平为一个循环,你应该从一开始就做到这一点。我也看不出这会导致丑陋或任何困难在维护代码时,计算索引有什么问题?在solve()
函数的i
循环之前设置#pragma omp parallel
,在进程()的i
循环之前设置孤#pragma omp for
指令
函数是否足以解决您的问题?@Gilles我已经尝试过这种方法,但是所有线程都使用相同的参数N,K执行了多次进程
。(可能我用错了这个语句。)请您提供一个@Gilles,您是对的,我在流程循环中使用了#pragma omp parallel for
。应该有pragma omp for
。这样结果是正确的,算法速度更快。(不如活动选项快,但值得在代码中应用。)这正是我所需要的。在我的实际情况中,我不能使用reduce。但是可以使用#pragma omp for reduce(+)nowait
进一步优化您的方法。在这种情况下,nowait
对大N*K也有很大的改进。无论如何,一切都基于您的初始解决方案。谢谢!