循环展开
概念
循环展开的核心思想是减少循环迭代次数,同时在每次迭代中执行更多的操作。
优势
-
减少分支预测开销
-
分支指令减少:循环条件判断次数显著降低
-
分支预测改善:更简单的控制流模式有利于预测器工作
-
流水线效率提升:减少流水线清空和气泡
-
- 提高指令级并行(ILP)
- 隐藏内存访问延迟
-
改善缓存性能
Perf
根据以上分析可知,最主要需要分析的就是branch与branch-misses
perf record -e branches,branch-misses ./benchmark
用例代码
测试以[1024]的向量乘法为例,分别对朴素实现与AVX实现尝试比较循环展开
void vector_mult_basic(const float* a, const float* b, float* result, size_t n) {
for (size_t i = 0; i < n; ++i) {
result[i] = a[i] * b[i];
}
return ;
}
void vector_mult_unrolled(const float* a, const float* b, float* result, size_t n) {
size_t i = 0;
// 主循环处理4的倍数个元素
for (; i + 3 < n; i += 4) {
result[i] = a[i] * b[i];
result[i+1] = a[i+1] * b[i+1];
result[i+2] = a[i+2] * b[i+2];
result[i+3] = a[i+3] * b[i+3];
}
// 处理剩余元素
for (; i < n; ++i) {
result[i] = a[i] * b[i];
}
return ;
}
void vector_mult_avx(const float* a, const float* b, float* result, size_t n) {
size_t i = 0;
// 新增:处理前面未对齐的部分
for (; (reinterpret_cast<uintptr_t>(a + i) % 32 != 0) && i < n; ++i) {
result[i] = a[i] * b[i];
}
// 主循环:使用对齐加载/存储
for (; i + 7 < n; i += 8) {
__m256 avx_a = _mm256_load_ps(a + i); // 对齐加载
__m256 avx_b = _mm256_load_ps(b + i);
__m256 avx_result = _mm256_mul_ps(avx_a, avx_b);
_mm256_store_ps(result + i, avx_result); // 对齐存储
}
for (; i < n; ++i) {
result[i] = a[i] * b[i];
}
return ;
}
void vector_mult_avx_unrolled(const float* a, const float* b, float* result, size_t n) {
size_t i = 0;
// 新增:处理前面未对齐的部分
for (; (reinterpret_cast<uintptr_t>(a + i) % 128 != 0) && i < n; ++i) {
result[i] = a[i] * b[i];
}
// 4倍循环展开,交错加载和计算以减少数据依赖
for (; i + 31 < n; i += 32) {
// 提前加载所有数据
__m256 avx_a1 = _mm256_load_ps(a + i);
__m256 avx_a2 = _mm256_load_ps(a + i + 8);
__m256 avx_a3 = _mm256_load_ps(a + i + 16);
__m256 avx_a4 = _mm256_load_ps(a + i + 24);
__m256 avx_b1 = _mm256_load_ps(b + i);
__m256 avx_b2 = _mm256_load_ps(b + i + 8);
__m256 avx_b3 = _mm256_load_ps(b + i + 16);
__m256 avx_b4 = _mm256_load_ps(b + i + 24);
// 执行乘法运算
__m256 avx_result1 = _mm256_mul_ps(avx_a1, avx_b1);
__m256 avx_result2 = _mm256_mul_ps(avx_a2, avx_b2);
__m256 avx_result3 = _mm256_mul_ps(avx_a3, avx_b3);
__m256 avx_result4 = _mm256_mul_ps(avx_a4, avx_b4);
// 存储结果
_mm256_store_ps(result + i, avx_result1);
_mm256_store_ps(result + i + 8, avx_result2);
_mm256_store_ps(result + i + 16, avx_result3);
_mm256_store_ps(result + i + 24, avx_result4);
}
// 剩余处理部分保持不变
for (; i + 7 < n; i += 8) {
__m256 avx_a = _mm256_load_ps(a + i);
__m256 avx_b = _mm256_load_ps(b + i);
__m256 avx_result = _mm256_mul_ps(avx_a, avx_b);
_mm256_store_ps(result + i, avx_result);
}
for (; i < n; ++i) {
result[i] = a[i] * b[i];
}
return ;
}
执行结果
| basic | basic_unrolled | avx | avx_unrolled | |
| branch | 106650631 | 5277378 | 2659714 | 16490 |
| branch-misses | 116481 | 66915 | 27262 | 6884 |
| time(us) | 1405 | 1019 | 419 | 367 |
循环展开与性能优化分析
70

被折叠的 条评论
为什么被折叠?



