AVX优化矩阵乘法索引访问变慢及Clang编译性能异常咨询
AVX指令集矩阵乘法优化实践与性能问题
我基于AVX指令集做矩阵乘法优化实践,写了简易的矩阵乘法基准测试代码,逐步叠加优化策略,探索能达到的性能上限,首先给出朴素实现版本,以及我写的最简AVX实现版本。
基础实现代码
朴素版矩阵乘法
void mmult_naive() { int i, j, k = 0; // 遍历矩阵A的每一行元素 for (i = 0; i < SIZE; i++) { // 遍历矩阵B的每一列元素 for (j = 0; j < SIZE; j++) { for (k = 0; k < SIZE; k++) { matrix_C[i][j] += matrix_A[i][k] * matrix_B[k][j]; } } } }
带B矩阵转置的AVX实现
我选择先对矩阵B做转置,方便把内存里连续存放的数据直接加载到向量寄存器,这个方案运行正常,达到了预期的加速效果,代码如下:
void mmult_avx_transposed() { __m256 row_vector_A; __m256 row_vector_B; int i, j, k = 0; __m256 int_prod; // 转置矩阵B transposeMatrix(); for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { int_prod = _mm256_setzero_ps(); for (k = 0; k < (SIZE / 8); k++) { row_vector_A = _mm256_load_ps(&matrix_A[i][k * 8]); row_vector_B = _mm256_load_ps(&T_matrix[j][k * 8]); int_prod = _mm256_fmadd_ps(row_vector_A, row_vector_B, int_prod); } matrix_C[i][j] = hsum_single_avx(int_prod); } } }
无转置逐索引构造向量的AVX实现
测试边长N>1024的N×N矩阵执行耗时时,我觉得如果能找到更合理的元素访问方式,或许可以省掉转置步骤——转置函数本身大概占总执行时间的4%~5%,属于很容易优化的性能点。
于是我删掉了transposeMatrix()转置步骤,把原代码里第二个_mm256_load_ps调用替换成了逐元素索引构造向量的代码:
row_vector_B = _mm256_setr_ps(matrix_B[k * 8][j], matrix_B[(k * 8) + 1][j], matrix_B[(k * 8) + 2][j], matrix_B[(k * 8) + 3][j], matrix_B[(k * 8) + 4][j], matrix_B[(k * 8) + 5][j], matrix_B[(k * 8) + 6][j], matrix_B[(k * 8) + 7][j]);
但替换之后代码运行性能反而暴跌。
性能测试结果
本次测试所有代码均在i7-8700处理器、Arch Linux系统下编译,编译参数为-O3、-mavx2、-march=native,使用的g++版本为12.1.0,clang版本为14.0.6。
GCC编译版本耗时
MMULT_NAIVE execution time: 195,499 us MMULT_AVX_TRANSPOSED execution time: 127,802 us MMULT_AVX_INDEXED execution time: 1,482,524 us
Clang编译版本耗时
我尝试用Clang编译器编译验证性能表现,结果性能反而更差:
MMULT_NAIVE execution time: 2,027,125 us MMULT_AVX_TRANSPOSED execution time: 125,781 us MMULT_AVX_INDEXED execution time: 1,798,410 us
待解答的两个技术疑问
- 为什么用逐索引构造向量的无转置AVX实现,运行速度远慢于转置版本,甚至比朴素实现还慢?
- 相同编译参数下,Clang编译出来的版本为什么性能异常,连朴素实现的运行速度都远低于GCC编译的对应版本?
内容的提问来源于stack exchange,提问作者Arkoudinos
相关产品推荐
相关产品推荐

