直接用 _mm256_mul_ps 写矩阵乘法比 sgemm 慢,因未对齐内存、无预取、未分块,而瓶颈在内存搬运与 cache 命中率;sgemm 通过 64 字节对齐、多级分块、prefetchw 和寄存器复用整体优化访存与计算节奏。

为什么直接用 _mm256_mul_ps 写矩阵乘法反而比 sgemm 慢?
因为没对齐 + 没预取 + 没分块,SIMD 指令只是加速“计算”,但矩阵乘真正的瓶颈在内存搬运和 cache 命中率。你手写的 _mm256_load_ps 如果读的是非 32 字节对齐的地址,会触发 #GP 异常或降级为慢速路径;而 OpenBLAS 的 sgemm 默认启用 64 字节对齐、多级分块、prefetchw、寄存器复用——它不是靠单条指令快,是靠整个访存+计算节奏压榨 CPU。
实操建议:
- 用
aligned_alloc(32, size)或_mm_malloc(size, 32)分配内存,别用new float[n] - 内层循环必须按 8(AVX)或 16(AVX-512)元素分组,否则
_mm256_load_ps读越界或漏数据 - 加
_mm_prefetch(&A[i+64], _MM_HINT_NTA)提前拉取下一块,否则 L1d cache 反复 miss
如何让自己的 SIMD 矩阵乘支持 batch 和 transpose 场景?
深度学习里 matmul 绝大多数不是纯 A×B,而是 B×A^T(如 attention 中 QK^T)或 batched([b, m, k] × [b, k, n])。硬写一个函数处理所有 layout,不如按场景拆:用模板参数或 enum 控制是否转置,用指针偏移模拟 batch。
常见错误现象:segmentation fault 在 batch > 1 时出现,大概率是步长(stride)算错,比如把 ldA = k 写成 ldA = m。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 定义统一接口:
simd_gemm(bool transA, bool transB, int M, int N, int K, const float* A, int ldA, const float* B, int ldB, float* C, int ldC) - transpose 不现场转,而是改访问模式:原
A[i*k + j]→transA ? A[j*m + i] : A[i*k + j] - batch 场景下,外层循环跑
b次,每次调用前更新A += b_stride、B += b_stride、C += b_stride
AVX-512 vs AVX2 在 int8 量化 matmul 中的实际收益差异
不是“开了 AVX-512 就一定快”。int8 矩阵乘核心是 _mm512_dpbusd_epi32(VNNI),但它要求输入是 uint8/int8、输出是 int32,且每组 4×4 乘加需严格满足 packed layout。如果你的数据是 NHWC 格式、或 scale/zero_point 存在 per-channel 差异,AVX-512 的向量化收益会被格式转换开销吃掉大半。
性能影响关键点:
- AVX2 下常用
_mm256_maddubs_epi16+_mm256_madd_epi16模拟,吞吐低但兼容性好(Skylake 及以后都行) - AVX-512 VNNI 指令在 Ice Lake 后才稳定支持,老 Xeon 上运行会 fallback 到 AVX2,甚至 crash
- int8 的 zero-point 补偿必须在 accum 之后做,否则溢出 —— 很多人把
zp_a * sum_b + zp_b * sum_a错写成逐元素加
怎么验证你的 SIMD matmul 真的被正确调用了?
编译器可能把你的 _mm256 内联函数全优化掉,或者生成了非预期的 shuffle 指令。光看结果对不上不顶用,得确认执行流真进了你的 kernel。
实操建议:
- 编译时加
-mavx2 -O2 -g,然后用objdump -d your_binary | grep "vpmaddwd\|vmulps"查看是否生成对应指令 - 在 kernel 入口插
asm volatile("nop" ::: "rax"),用 perf record -e instructions:u 抓采样,确认 hot 函数命中你的符号 - 用
std::chrono测单次耗时,但必须 warmup 至少 100 次再计时,否则第一次会因 TLB/cache 未就绪严重失真
最常被忽略的是数据布局假设 —— 你以为传进去的是 row-major,实际框架(如 PyTorch)可能给你的是 packed weight 或 4-bit block-wise layout。没对上格式,SIMD 写得再漂亮也白搭。










