C++ 矩阵乘法自动向量化
Posted
技术标签:
【中文标题】C++ 矩阵乘法自动向量化【英文标题】:C++ Matrix Multiplication Auto-Vectorization 【发布时间】:2017-04-20 07:25:07 【问题描述】:我启用了自动矢量化。当我编译代码时,我收到以下警告:
info C5002: loop not vectorized due to reason '1203'
MSDN 指定
循环体包括对数组的非连续访问。
我查看了这些链接,1,2,寻求帮助,但没有运气。
这是我的源代码:
for (int row = 0; row < size; ++row)
for (int col = 0; col < size; ++col)
float tmp = 0;
for (int i = 0; i < size; ++i) // This loop generates the warning above
tmp += matrixA[row][i] * matrixB[i][col];
matrixResult[row][col] = tmp;
欢迎任何帮助。
【问题讨论】:
C++ 二维数组在内存中排列为一维数组 row1、row2 等。这个表达式matrixB[i][col]
导致索引在数组中跳转。这个表达式matrixA[row][i]
没有。
您的矩阵是如何定义/分配的?如果他们是double **
,那么由于缓存位置问题,您的性能也会很差。
先转置 B(并交换索引),以便获得连续访问。
只是为了澄清一下:有一些答案(和 MSFT 编译器消息有点暗示相同)是不可能矢量化(使用编译器)给定循环以防万一或不同的访问顺序,并且由于不连续(非单位)步幅。这基本上是错误的。可以按原样对代码进行矢量化,但是(a)在许多平台上它可能是无利可图的(可能会变慢),特别是没有有效的收集指令导入,(b)某些编译器中的一些矢量化器可能是无法矢量化此类代码,但 gcc/icc 应该能够使用 omp4.x 显式矢量化它
但是我上面的评论自然只是为了完整性,并没有改变关键信息,即:在这段代码中保持内存访问是低效的。
【参考方案1】:
2D 数组存储为单个连续的内存块,因此 3x2 元素的 2D 数组实际上是首尾相连的 6 个元素。
[] 索引运算符只是计算要访问的元素。
所以这里发生的情况是,matrixA 被顺序地从元素 1 访问到元素 6(即 A1、A2、A3、B1、B2、B3)。
但是,matrixB 被“随机”访问,A1、B1、A2、B2 等映射到实际存储上,作为访问元素 1 然后 4 然后 2 然后 5。
您无法更改访问 matrixB 元素的顺序,但您可以转置它,以便按正确的顺序访问元素。显然,如果只进行一次乘法运算,可能不值得重新计算矩阵 B 的顺序,但如果重复执行此计算,那么付出的努力将非常值得。
【讨论】:
【参考方案2】:如果矩阵 A
和 B
具有相同的存储顺序(例如行主要),那么无论如何您都无法对其进行向量化。所以这使得警告是合理的。
这里只是一个建议:如果您想要认真的高性能计算,那么您应该放弃二维数组。缓存的收益远大于矢量化速度的提升。
【讨论】:
【参考方案3】:实现连续访问的一种方法:您可以交换内部的两个循环。而不是for row, for col, for i
你有for row, for i, for col
。请参阅下面的结果代码。现在matrixResult
和matrixB
的访问都沿着col
,所以是连续的。
for (int row = 0; row < size; ++row)
for (int i = 0; i < size; ++i)
int a_row_i = matrixA[row][i];
for (int col = 0; col < size; ++col)
matrixResult[row][col] += a_row_i * matrixB[i][col];
【讨论】:
以上是关于C++ 矩阵乘法自动向量化的主要内容,如果未能解决你的问题,请参考以下文章
当数组是函数参数时,矩阵乘法中的 Gcc 自动向量化奇怪行为
使用 openmp 并行化矩阵乘法并使用 avx2 进行矢量化