如何使用Intel Intrinsics在C语言中实现向量化?附代码优化需求
基于SSE 4.1/4.2的C程序向量化入门及示例优化
先理清原代码的可优化点
原代码里的pow(2, k)是核心性能瓶颈——这是通用数学库函数,针对整数次幂完全没必要调用。2^k可以通过递推乘法实现:初始系数为2.0f,每次循环乘以2即可,速度快几个数量级。另外,原代码存在变量名拼写错误Vl,统一修正为VL。
SSE向量化的核心思路
SSE 4.1/4.2的128位寄存器一次能处理4个32位float,我们可以把循环拆成「批量处理4个元素」和「处理剩余不足4个的元素」两部分,用Intel Intrinsics指令直接操作向量寄存器,提升计算吞吐量。
关键Intrinsics函数说明:
__m128:SSE的128位向量类型,存储4个float值_mm_setzero_ps():初始化向量为全0_mm_loadu_ps():从非对齐内存加载4个float(若数组是16字节对齐的,用_mm_load_ps()更快)_mm_mul_ps(a, b):两个向量逐元素相乘_mm_add_ps(a, b):两个向量逐元素相加_mm_hadd_ps(a, b):水平加法,将向量内元素两两求和(用于最终的向量总和计算)_mm_set1_ps(x):创建所有元素都是x的向量(用于批量更新系数)
向量化后的完整代码
#include <xmmintrin.h> // SSE基础头文件 #include <smmintrin.h> // SSE 4.1扩展头文件 #include <math.h> // 剩余元素处理用powf float vectorized_function(float* Array, int Initial, int Finishing_point) { int count = Finishing_point - Initial; if (count <= 0) return 0.0f; __m128 sum_vec = _mm_setzero_ps(); // 向量累加器,初始全0 int i = 0; // 批量处理4个元素的循环 const int batch_size = 4; int num_batches = count / batch_size; // 初始系数向量:对应k=1,2,3,4的2^k → 2,4,8,16(注意SSE寄存器低位对应数组先出现的元素) __m128 coeff_vec = _mm_set_ps(16.0f, 8.0f, 4.0f, 2.0f); for (; i < num_batches * batch_size; i += batch_size) { // 加载当前4个数组元素 __m128 arr_vec = _mm_loadu_ps(Array + Initial + i); // 数组元素 × 对应系数 __m128 mul_vec = _mm_mul_ps(arr_vec, coeff_vec); // 累加到总和向量 sum_vec = _mm_add_ps(sum_vec, mul_vec); // 更新系数:下一组k=5~8,对应2^5=2*16, 2^6=4*16... 整体乘以16(2^4) coeff_vec = _mm_mul_ps(coeff_vec, _mm_set1_ps(16.0f)); } // 处理剩余不足4个的元素(标量循环) float scalar_sum = 0.0f; float current_coeff = powf(2.0f, i + 1); // 剩余元素的起始系数 for (; i < count; i++) { scalar_sum += Array[Initial + i] * current_coeff; current_coeff *= 2.0f; } // 把向量累加器的4个元素求和 sum_vec = _mm_hadd_ps(sum_vec, sum_vec); // 第一次水平加法:[a,b,c,d] → [a+b, c+d, a+b, c+d] sum_vec = _mm_hadd_ps(sum_vec, sum_vec); // 第二次水平加法:[a+b+c+d, a+b+c+d, ...] float vector_sum = _mm_cvtss_f32(sum_vec); // 提取向量的第一个元素(总和) // 向量总和 + 剩余元素的标量总和 return vector_sum + scalar_sum; }
编译说明
- GCC/Clang:添加编译选项
-msse4.1 - MSVC:添加编译选项
/arch:SSE4.1
额外优化建议
- 如果数组是静态分配或可控制内存对齐,用
__attribute__((aligned(16))) float Array[...];声明数组,将_mm_loadu_ps()替换为_mm_load_ps(),进一步提升内存加载速度。 - 剩余元素的系数可从批量处理的最后一个系数递推,避免调用
powf函数,比如在批量循环结束后,提取coeff_vec的最后一个元素作为剩余元素的起始系数。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

