竞争条件会降低代码的性能吗?

时间:2016-01-19 22:02:19

标签: c++ multithreading performance parallel-processing openmp

我正在运行以下代码来进行矩阵乘法我应该测量的性能:

for (int j = 0; j < COLUMNS; j++)
#pragma omp for schedule(dynamic, 10)
    for (int k = 0; k < COLUMNS; k++)
        for (int i = 0; i < ROWS; i++)
            matrix_r[i][j] += matrix_a[i][k] * matrix_b[k][j];

是的,我知道它真的很慢,但这不是重点 - 它纯粹是出于性能测量的目的。我正在运行3个版本的代码,具体取决于我放置#pragma omp指令的位置,因此取决于并行化发生的位置。代码在Microsoft Visual Studio 2012中以发布模式运行,并在CodeXL中进行概要分析。

我从测量中注意到的一件事是代码片段中的选项(在k循环之前具有并行化)是最慢的,然后是在j循环之前带有指令的版本,然后是在之前的带有它的那个。我循环。由于竞争条件,所呈现的版本也是计算错误结果的版本 - 多个线程同时访问结果矩阵的同一单元格。我理解为什么i循环版本是最快的 - 所有特定的线程只处理i变量范围的一部分,增加了时间局部性。但是,我不明白是什么原因导致k循环版本最慢 - 它是否与它产生错误结果的事实有关?

2 个答案:

答案 0 :(得分:3)

当然竞争条件会降低代码速度。当两个或多个线程访问相同的内存部分(相同的缓存行)时,必须一遍又一遍地将该部分加载到给定内核的缓存中,因为另一个线程通过写入缓存来使缓存的内容无效。他们争夺共享资源。

当内存中位置太近的两个变量被更多线程写入和读取时,它也会导致速度减慢。这称为false sharing。在你的情况下,更糟糕的是,它们不仅太紧密,它们甚至会重合。

答案 1 :(得分:1)

你的假设是正确的。但如果我们谈论的是绩效,而不仅仅是验证你的假设,那么故事还有更多。

  • 索引的顺序是个大问题,多线程与否。假设mat[x][y]mat[x][y+1]之间的距离为1,而mat[x][y]mat[x+1][y]之间的距离为dim(mat[x])您希望xy外部索引和__[i][j] += __[i][k] * __[k][j];内部具有迭代之间的最小距离。鉴于i -> k -> j,您会发现空间位置的正确顺序为for (int j = 0; j < COLUMNS; j++) for (int k = 0; k < COLUMNS; k++) for (int i = 0; i < ROWS; i++) matrix_r[i][j] += matrix_a[i][k] * matrix_b[k][j];
  • 无论订单如何,都有一个值可以保存以供日后使用。给出你的代码片段

    matrix_b[k][j]

i值将从内存 for (int j = 0; j < COLUMNS; j++) for (int k = 0; k < COLUMNS; k++) int temp = matrix_b[k][j]; for (int i = 0; i < ROWS; i++) matrix_r[i][j] += matrix_a[i][k] * temp; 次获取。你可以从

开始
matrix_r[i][j]

但鉴于您正在写matrix_r[i][j],因此优化的最佳权限是for (int i = 0; i < ROWS; i++) matrix_r[i][j] += matrix_a[i][k] * matrix_b[k][j]; ,因为写作速度比阅读慢

对内存进行不必要的写访问

matrix_r[i][j]

将写入ROWS for (int i = 0; i < ...; j++) for (int j = 0; j < ...; k++) int temp = 0; for (int k = 0; k < ...; i++) temp += matrix_a[i][k] * matrix_b[k][j]; matrix_r[i][j] = temp; 次内存。使用临时变量会减少对一个的访问。

matrix_b

这减少了从n ^ 3到n ^ 2的写访问。

  • 现在您正在使用线程。为了最大化多线程的效率,您应该从其他线程中隔离尽可能多的线程内存访问。一种方法是给每个线程一个列,并使该列完成一次。一种简单的方法是将matrix_r[i][j] += matrix_a[i][k] * matrix_b[k][j]; becomes matrix_r[i][j] += matrix_a[i][k] * matrix_b_trans[j][k]; 转置为

    matrix_a

这样k上最内部的循环总是处理与matrix_b_trans for (int i = 0; i < ROWS; j++) for (int j = 0; j < COLS; k++) int temp = 0; for (int k = 0; k < SAMEDIM; i++) temp += matrix_a[i][k] * matrix_b_trans[j][k]; matrix_r[i][j] = temp;

相对应的连续内存
@RenderSection("scripts", required: false)