我想用openMP优化下面的代码
double val;
double m_y = 0.0f;
double m_u = 0.0f;
double m_v = 0.0f;
#define _MSE(m, t) \
val = refData[t] - calData[t]; \
m += val*val;
#pragma omp parallel
{
#pragma omp for
for( i=0; i<(width*height)/2; i++ ) { //yuv422: 2 pixels at a time
_MSE(m_u, 0);
_MSE(m_y, 1);
_MSE(m_v, 2);
_MSE(m_y, 3);
#pragma omp reduction(+:refData) reduction(+:calData)
refData += 4;
calData += 4;
// int id = omp_get_thread_num();
//printf("Thread %d performed %d iterations of the loop\n",id ,i);
}
}任何建议,欢迎优化上述代码,目前我有错误的输出。
发布于 2013-02-11 10:24:26
我认为你能做的最简单的事情就是把它分成4个线程,然后计算每个线程的UYVY误差。不是让它们分隔值,而是使它们成为数组:
double sqError[4] = {0};
const int numBytes = width * height * 2;
#pragma omp parallel for
for( int elem = 0; elem < 4; elem++ ) {
for( int i = elem; i < numBytes; i += 4 ) {
int val = refData[i] - calData[i];
sqError[elem] += (double)(val*val);
}
}这样,每个线程只在一件事上操作,没有争用。
也许这不是OMP最高级的用法,但您应该会看到加速。
在你关于性能命中的评论之后,我做了一些实验,发现性能确实更差。我怀疑这可能是由于缓存未命中造成的。
你说过:
这次使用openMP时
性能受到影响:时间:0.040637,串行时间:0.018670
因此,我对每个变量进行了简化,并使用了一个循环:
#pragma omp parallel for reduction(+:e0) reduction(+:e1) reduction(+:e2) reduction(+:e3)
for( int i = 0; i < numBytes; i += 4 ) {
int val = refData[i] - calData[i];
e0 += (double)(val*val);
val = refData[i+1] - calData[i+1];
e1 += (double)(val*val);
val = refData[i+2] - calData[i+2];
e2 += (double)(val*val);
val = refData[i+3] - calData[i+3];
e3 += (double)(val*val);
}使用我在4核机器上的测试用例,我观察到略低于4倍的性能提升:
serial: 2025 ms
omp with 2 loops: 6850 ms
omp with reduction: 455 ms编辑在谈到为什么第一段代码比非并行版本表现更差时,Hristo Iliev说:
你的第一段代码是一个可怕的例子,说明了假共享在多线程代码中的作用。由于sqError只有4个元素,每个元素8个字节,所以它适合单个缓存线(甚至在现代x86 CPU上的一半缓存线)。由于4个线程不断地写入相邻元素,这将由于错误共享而生成大量的内核间缓存无效。可以通过使用像这样的结构_error { double val;double pad7;}sqError4来解决这个问题;现在每个sqErrori.val将在单独的缓存线中,因此没有错误的共享。
发布于 2013-02-11 10:16:42
代码看起来像是在计算均方误差,但是加上了相同的和m。为了让并行性正常工作,你需要消除m的共享,一种方法是预先分配一个数组(我想是宽*高/2)来存储不同的和,或者ms。最后,把所有的和加起来。
另外,测试一下这实际上更快!
https://stackoverflow.com/questions/14804859
复制相似问题