我正在运行以下代码来进行矩阵乘法我应该测量的性能:
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循环版本最慢 - 它是否与它产生错误结果的事实有关?
答案 0 :(得分:3)
当然竞争条件会降低代码速度。当两个或多个线程访问相同的内存部分(相同的缓存行)时,必须一遍又一遍地将该部分加载到给定内核的缓存中,因为另一个线程通过写入缓存来使缓存的内容无效。他们争夺共享资源。
当内存中位置太近的两个变量被更多线程写入和读取时,它也会导致速度减慢。这称为false sharing。在你的情况下,更糟糕的是,它们不仅太紧密,它们甚至会重合。
答案 1 :(得分:1)
你的假设是正确的。但如果我们谈论的是绩效,而不仅仅是验证你的假设,那么故事还有更多。
mat[x][y]
和mat[x][y+1]
之间的距离为1,而mat[x][y]
和mat[x+1][y]
之间的距离为dim(mat[x])
您希望x
为y
外部索引和__[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)