1

我想使用 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);
}

}

欢迎任何关于优化上述代码的建议,目前我有错误的输出。

4

2 回答 2

2

我认为您可以做的最简单的事情是让它分成 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 个线程不断写入相邻元素,这会由于错误共享而产生大量的内核间缓存失效。可以通过使用像这样的结构来解决这个问题 struct _error { double val; 双垫[7];} sqError[4]; 现在每个 sqError[i].val 都将位于单独的缓存行中,因此不会出现错误共享。

于 2013-02-11T02:24:26.117 回答
0

该代码看起来像是在计算 MSE,但添加到相同的总和中,m. 为了使并行性正常工作,您需要消除 的共享m,一种方法是预先分配一个数组(我想是宽度*高度/2)来存储不同的总和或ms。最后,把所有的总和加起来。

此外,测试这实际上更快!

于 2013-02-11T02:16:41.700 回答