Ale*_*hou 10 c performance vector matrix openmp
我是OpenMp编程的新手.我写了一个简单的c程序来将矩阵与向量相乘.不幸的是,通过比较执行时间,我发现OpenMP比Sequential方式慢得多.
这是我的代码(这里的矩阵是N*N int,vector是N int,结果是N long long):
#pragma omp parallel for private(i,j) shared(matrix,vector,result,m_size)
for(i=0;i<m_size;i++)
{
for(j=0;j<m_size;j++)
{
result[i]+=matrix[i][j]*vector[j];
}
}
Run Code Online (Sandbox Code Playgroud)
这是顺序方式的代码:
for (i=0;i<m_size;i++)
for(j=0;j<m_size;j++)
result[i] += matrix[i][j] * vector[j];
Run Code Online (Sandbox Code Playgroud)
当我使用999x999矩阵和999向量尝试这两个实现时,执行时间为:
顺序:5439 ms并行:11120 ms
我真的不明白为什么OpenMP比顺序算法慢得多(慢2倍!)任何人都可以解决我的问题?
Hri*_*iev 15
您的代码部分遭受所谓的错误共享,这是所有缓存一致系统的典型代表.简而言之,result[]阵列的许多元素都适合同一个缓存行.当线程作为运算符的结果i写入时,保存该部分的高速缓存行变脏.然后,高速缓存一致性协议使其他核心中的该高速缓存行的所有副本无效,并且它们必须从高级高速缓存或主存储器刷新其副本.作为一个数组,然后一个缓存行(x86上的64个字节)包含8个元素,此外在同一缓存行中还有7个其他数组元素.因此,两个"相邻"线程可能会不断争夺高速缓存行的所有权(假设每个线程在一个单独的核心上运行).result[i]+=result[]resultlong longresult[i]
为了减轻您的情况下的错误共享,最简单的方法是确保每个线程都获得一个迭代块,其大小可以被缓存行中的元素数量整除.例如,您可以将schedule(static,something*8)where something应该足够大,以便迭代空间不会碎片化为太多碎片,但同时它应该足够小,以便每个线程获得一个块.例如,对于m_size等于999和4个线程,您可以将该schedule(static,256)子句应用于parallel for构造.
代码运行速度较慢的另一个部分原因可能是,当启用OpenMP时,编译器可能不愿意在分配共享变量时应用某些代码优化.OpenMP提供了所谓的宽松内存模型,允许每个线程中共享变量的本地内存视图不同,并flush提供构造以同步视图.但是如果编译器volatile无法证明其他线程不需要访问去同步的共享变量,那么通常会将共享变量视为隐式变量.你的情况就是其中之一,因为result[i]它只被分配给,而result[i]其他线程从不使用它的值.在串行情况下,编译器很可能会创建一个临时变量来保存内部循环的结果,并且只会result[i]在内部循环完成后分配.在并行的情况下,它可能会决定这会result[i]在其他线程中创建一个临时的同步视图,从而决定不应用优化.仅仅为了记录,GCC 4.7.1与-O3 -ftree-vectorize启用OpenMP的临时变量技巧有关.
因为当 OpenMP 在线程之间分配工作时,会进行大量管理/同步,以确保共享矩阵和向量中的值不会以某种方式损坏。即使它们是只读的:人类很容易看到,但您的编译器可能不会。
出于教学原因可以尝试的事情:
0) 如果matrix和vector不是,会发生什么shared?
1) 首先并行化内部“j-loop”,保持外部“i-loop”串行。走着瞧吧。
2) 不要将总和收集到 中result[i],而是收集到一个变量中,并仅在内循环完成后将temp其内容分配给,以避免重复索引查找。result[i]不要忘记temp在内循环开始之前将其初始化为 0。