行主序的GEMM优化方案
克隆高叔叔的X86浮点峰值测试工具,使用方法见README。
本机(Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz 12 Cores)测试结果如下:
Thread(s): 1
fma fp32 perf: 97.3358 gflops.
fma fp64 perf: 48.8463 gflops.
avx fp32 perf: 48.7343 gflops.
avx fp64 perf: 24.3121 gflops.
sse fp32 perf: 25.9305 gflops.
sse fp64 perf: 12.9604 gflops.
Thread(s): 4
fma fp32 perf: 356.8164 gflops.
fma fp64 perf: 178.6532 gflops.
avx fp32 perf: 178.6297 gflops.
avx fp64 perf: 89.3076 gflops.
sse fp32 perf: 93.3519 gflops.
sse fp64 perf: 46.6798 gflops.
测试不同的优化方法对应的gflops。
峰值占比的计算方法为当前算法的gflops除以浮点测试工具中相同线程测出的最高峰值。
文件名 | 优化方法 | gFLOPs | 峰值占比 | 线程数 |
---|---|---|---|---|
MMult1.h | 无任何优化 | 1.4gflops | 1.4% | 1 |
MMult2.h | 一次计算4个元素 | 1.5gflops | 1.5% | 1 |
MMult_1x4_3.h | 一次计算4个元素 | 1.4gflops | 1.4% | 1 |
MMult_1x4_4.h | 一次计算4个元素 | 1.4gflops | 1.4% | 1 |
MMult_1x4_5.h | 一次计算4个元素(将4个循环合并为1个) | 1.5gflops | 1.5% | 1 |
MMult_1x4_6.h | 一次计算4个元素(我们在寄存器中累加C的元素,并对a的元素使用寄存器) | 1.6gflops | 1.6% | 1 |
MMult_1x4_7.h | 在MMult_1x4_6的基础上用指针来寻址B中的元素 | 5.0gflops | 6% | 1 |
MMult_1x4_8.h | 在MMult_1x4_7的基础上循环展开四个(展开因子的相对任意选择) | 5gflops | 6% | 1 |
MMult_1x4_9.h | 在MMult_1x4_8的基础上使用间接寻址的方法 | 5gflops | 6% | 1 |
MMult_4x4_3.h | 一次计算C中的4x4小块 | 1.4gflops | 1.4% | 1 |
MMult_4x4_4.h | 一次计算C中的4x4小块 | 1.4gflops | 1.4% | 1 |
MMult_4x4_5.h | 一次计算C中的4x4小块,将16个循环合并一个 | 1.5gflops | 1.5% | 1 |
MMult_4x4_6.h | 一次计算C中的4x4小块(我们在寄存器中累加C的元素,并对a的元素使用寄存器) | 8.2gflops | 8.4% | 1 |
MMult_4x4_7.h | 在MMult_4x4_6的基础上用指针来寻址B中的元素 | 8.4gflops | 8.6% | 1 |
MMult_4x4_8.h | 使用更多的寄存器 | 7.7gflops | 7.7% | 1 |
MMult_4x4_10.h | SSE指令集优化 | 8.5gflops | 8.7% | 1 |
MMult_4x4_11.h | SSE指令集优化, 并且为了保持较小问题规模所获得的性能,我们分块矩阵C(以及相应的A和B) | 8.5gflops | 8.7% | 1 |
MMult_4x4_13.h | SSE指令集优化, 对矩阵A和B进行Pack,这样就可以连续访问内存 | 33.0gflops | 34.0% | 1 |
理论浮点峰值:
fmla: 4x2(mul+add)*1.8gHz=14.4gFLOPs
实际浮点峰值测试
|架构|浮点峰值(GFlops)|
|--|--|
|Cortex-A53,armv7a|10.88|
达到硬件浮点峰值的75%
文件名 | 优化方法 | gFLOPs | 峰值占比 | 线程数 |
---|---|---|---|---|
MMult1.h | 无任何优化 | 0.24gflops | 2.1% | 1 |
MMult2.h | 一次计算4个元素 | 0.24gflops | 2.1% | 1 |
MMult_1x4_3.h | 一次计算4个元素 | 0.24gflops | 2.1% | 1 |
MMult_1x4_4.h | 一次计算4个元素 | 0.24gflops | 2.1% | 1 |
MMult_1x4_5.h | 一次计算4个元素(将4个循环合并为1个) | 0.25gflops | 2.2% | 1 |
MMult_1x4_7.h | 一次计算4个元素(我们在寄存器中累加C的元素,并对a的元素使用寄存器),用指针来寻址B中的元素 | 0.98gflops | 9.0% | 1 |
MMult_1x4_8.h | 在MMult_1x4_7的基础上循环展开四个(展开因子的相对任意选择) | 1.1gflops | 10% | 1 |
MMult_4x4_3.h | 一次计算C中的4x4小块 | 0.24gflops | 2.1% | 1 |
MMult_4x4_4.h | 一次计算C中的4x4小块 | 0.24gflops | 2.1% | 1 |
MMult_4x4_5.h | 一次计算C中的4x4小块,将16个循环合并一个 | 0.25gflops | 2.2% | 1 |
MMult_4x4_6.h | 一次计算C中的4x4小块(我们在寄存器中累加C的元素,并对a的元素使用寄存器) | 1.75gflops | 16.0% | 1 |
MMult_4x4_7.h | 在MMult_4x4_6的基础上用指针来寻址B中的元素 | 1.75gflops | 16.0% | 1 |
MMult_4x4_8.h | 使用更多的寄存器 | 1.75gflops | 16.0% | 1 |
MMult_4x4_10.h | NEON指令集优化 | 2.6gflops | 23.8% | 1 |
MMult_4x4_11.h | NEON指令集优化, 并且为了保持较小问题规模所获得的性能,我们分块矩阵C(以及相应的A和B) | 2.6gflops | 23.8% | 1 |
MMult_4x4_13.h | NEON指令集优化, 对矩阵A和B进行Pack,这样就可以连续访问内存 | 2.6gflops | 23.8% | 1 |
conv1x1s1.h(version1) | 一次计算多行,neon汇编优化 | 3.4gflops | 31.0% | 1 |
conv1x1s1.h(version2) | pack,kernel提前做,neon汇编优化 | 4.9gflops | 45% | 1 |
conv1x1s1.h(version3) | pack,kernel提前做,输入NC4HW4,neon汇编优化 | 5.5gflops | 50.5% | 1 |