SSE2指令集
SSE2指令集常用函数
// 数据加载
// 对齐加载
__m128i _mm_load_si128(const __m128i *mem_addr); // 从对齐地址加载
__m128d _mm_load_pd(const double *mem_addr); // 加载双精度浮点数
// 非对齐加载
__m128i _mm_loadu_si128(const __m128i *mem_addr); // 从任意地址加载
__m128d _mm_loadu_pd(const double *mem_addr); // 非对齐加载双精度
// 数据存储
// 对齐存储
void _mm_store_si128(__m128i *mem_addr, __m128i a); // 存储到对齐地址
void _mm_store_pd(double *mem_addr, __m128d a); // 存储双精度
// 非对齐存储
void _mm_storeu_si128(__m128i *mem_addr, __m128i a); // 存储到任意地址
void _mm_storeu_pd(double *mem_addr, __m128d a); // 非对齐存储双精度
// 算术运算
// 加法
__m128i _mm_add_epi8(__m128i a, __m128i b); // 8位整数加法
__m128i _mm_add_epi16(__m128i a, __m128i b); // 16位整数加法
__m128i _mm_add_epi32(__m128i a, __m128i b); // 32位整数加法
__m128i _mm_add_epi64(__m128i a, __m128i b); // 64位整数加法
// 减法
__m128i _mm_sub_epi8(__m128i a, __m128i b); // 8位整数减法
__m128i _mm_sub_epi16(__m128i a, __m128i b); // 16位整数减法
__m128i _mm_sub_epi32(__m128i a, __m128i b); // 32位整数减法
__m128i _mm_sub_epi64(__m128i a, __m128i b); // 64位整数减法
// 浮点数
__m128d _mm_add_pd(__m128d a, __m128d b); // 双精度加法
__m128d _mm_sub_pd(__m128d a, __m128d b); // 双精度减法
__m128d _mm_mul_pd(__m128d a, __m128d b); // 双精度乘法
__m128d _mm_div_pd(__m128d a, __m128d b); // 双精度除法
// 逻辑运算
__m128i _mm_and_si128(__m128i a, __m128i b); // 位与
__m128i _mm_or_si128(__m128i a, __m128i b); // 位或
__m128i _mm_xor_si128(__m128i a, __m128i b); // 位异或
__m128i _mm_andnot_si128(__m128i a, __m128i b); // 位与非
// 比较运算
// 整数
__m128i _mm_cmpeq_epi8(__m128i a, __m128i b); // 8位相等比较
__m128i _mm_cmpeq_epi16(__m128i a, __m128i b); // 16位相等比较
__m128i _mm_cmpeq_epi32(__m128i a, __m128i b); // 32位相等比较
__m128i _mm_cmpgt_epi8(__m128i a, __m128i b); // 8位大于比较
__m128i _mm_cmpgt_epi16(__m128i a, __m128i b); // 16位大于比较
__m128i _mm_cmpgt_epi32(__m128i a, __m128i b); // 32位大于比较
// 浮点数
__m128d _mm_cmpeq_pd(__m128d a, __m128d b); // 双精度相等比较
__m128d _mm_cmpgt_pd(__m128d a, __m128d b); // 双精度大于比较
__m128d _mm_cmplt_pd(__m128d a, __m128d b); // 双精度小于比较
// 移位运算
// 逻辑位移
__m128i _mm_slli_epi16(__m128i a, int imm8); // 16位左移
__m128i _mm_slli_epi32(__m128i a, int imm8); // 32位左移
__m128i _mm_slli_epi64(__m128i a, int imm8); // 64位左移
__m128i _mm_srli_epi16(__m128i a, int imm8); // 16位右移
__m128i _mm_srli_epi32(__m128i a, int imm8); // 32位右移
__m128i _mm_srli_epi64(__m128i a, int imm8); // 64位右移
__m128i _mm_slli_si128(__m128i a, int imm8); // 对 128 位数据进行字节级别的逻辑左移
// 算术位移
__m128i _mm_srai_epi16(__m128i a, int imm8); // 16位算术右移
__m128i _mm_srai_epi32(__m128i a, int imm8); // 32位算术右移
// 数据重排
// 洗牌
__m128i _mm_shuffle_epi32(__m128i a, int imm8); // 32位元素重排
__m128d _mm_shuffle_pd(__m128d a, __m128d b, int imm8); // 双精度重排
// 解包
__m128i _mm_unpackhi_epi8(__m128i a, __m128i b); // 高位解包8位
__m128i _mm_unpacklo_epi8(__m128i a, __m128i b); // 低位解包8位
__m128i _mm_unpackhi_epi16(__m128i a, __m128i b); // 高位解包16位
__m128i _mm_unpacklo_epi16(__m128i a, __m128i b); // 低位解包16位
// 设置常量
__m128i _mm_setzero_si128(void); // 设置全零
__m128i _mm_set1_epi8(char a); // 设置所有8位元素
__m128i _mm_set1_epi16(short a); // 设置所有16位元素
__m128i _mm_set1_epi32(int a); // 设置所有32位元素
__m128i _mm_set_epi8(char e15, ..., char e0); // 设置16个8位元素
__m128i _mm_set_epi32(int e3, int e2, int e1, int e0); // 设置4个32位元素
1. 简单案例
#include <emmintrin.h> // SSE2 指令集头文件
void example() {
// 1. 初始化源数据数组
int data[4] = {1, 2, 3, 4};
// 2. 从内存加载数据到 SSE 寄存器
// _mm_loadu_si128 从非对齐内存地址加载 128 位数据
__m128i vec = _mm_loadu_si128((__m128i*)data);
// 3. 执行 SSE 操作
// _mm_set1_epi32(10) 创建包含4个10的128位向量:{10, 10, 10, 10}
// _mm_add_epi32 执行4个32位整数的并行加法
vec = _mm_add_epi32(vec, _mm_set1_epi32(10));
// 4. 将结果存储回内存
int result[4];
_mm_storeu_si128((__m128i*)result, vec);
// 5. 结果:result 现在包含 {11, 12, 13, 14}
}
2. 完整案例
#include <emmintrin.h>
#include <stdio.h>
#include <stdalign.h> // 用于内存对齐
#include <stdbool.h>
// 打印 __m128i 变量的内容(32位整数)
void print_m128i_epi32(__m128i value, const char* name) {
alignas(16) int result[4];
_mm_store_si128((__m128i*)result, value);
printf("%s: {%d, %d, %d, %d}\n", name, result[0], result[1], result[2], result[3]);
}
// 基本示例:向量加法
void basic_example() {
printf("=== 基本示例:向量加法 ===\n");
// 源数据
int data[4] = {1, 2, 3, 4};
printf("原始数据: {%d, %d, %d, %d}\n", data[0], data[1], data[2], data[3]);
// 加载到 SSE 寄存器
__m128i vec = _mm_loadu_si128((__m128i*)data);
print_m128i_epi32(vec, "加载后的向量");
// 创建常量向量 {10, 10, 10, 10}
__m128i constant = _mm_set1_epi32(10);
print_m128i_epi32(constant, "常量向量");
// 执行向量加法
__m128i result_vec = _mm_add_epi32(vec, constant);
print_m128i_epi32(result_vec, "加法结果");
// 存储回内存
int result[4];
_mm_storeu_si128((__m128i*)result, result_vec);
printf("最终结果: {%d, %d, %d, %d}\n", result[0], result[1], result[2], result[3]);
}
// 进阶示例:多种 SSE2 操作
void advanced_example() {
printf("\n=== 进阶示例:多种 SSE2 操作 ===\n");
int a[4] = {10, 20, 30, 40};
int b[4] = {5, 6, 7, 8};
__m128i vec_a = _mm_loadu_si128((__m128i*)a);
__m128i vec_b = _mm_loadu_si128((__m128i*)b);
printf("向量 A: {%d, %d, %d, %d}\n", a[0], a[1], a[2], a[3]);
printf("向量 B: {%d, %d, %d, %d}\n", b[0], b[1], b[2], b[3]);
// 加法
__m128i add_result = _mm_add_epi32(vec_a, vec_b);
print_m128i_epi32(add_result, "A + B");
// 减法
__m128i sub_result = _mm_sub_epi32(vec_a, vec_b);
print_m128i_epi32(sub_result, "A - B");
// 乘法(注意:没有直接的32位整数乘法,这里使用16位演示)
__m128i mul_result = _mm_mullo_epi16(
_mm_set_epi16(10, 20, 30, 40, 50, 60, 70, 80),
_mm_set_epi16(2, 3, 4, 5, 6, 7, 8, 9)
);
// 位运算
__m128i and_result = _mm_and_si128(vec_a, vec_b);
print_m128i_epi32(and_result, "A & B (位与)");
__m128i or_result = _mm_or_si128(vec_a, vec_b);
print_m128i_epi32(or_result, "A | B (位或)");
}
// 性能对比示例:标量 vs 向量运算
void performance_example() {
printf("\n=== 性能对比示例(修正版) ===\n");
const int SIZE = 10000; // 减小数据量用于演示
const int VECTOR_SIZE = SIZE + 3; // 确保是4的倍数
// 使用动态内存分配并手动对齐
int* array1 = (int*)aligned_alloc(16, VECTOR_SIZE * sizeof(int));
int* array2 = (int*)aligned_alloc(16, VECTOR_SIZE * sizeof(int));
int* result_scalar = (int*)aligned_alloc(16, VECTOR_SIZE * sizeof(int));
int* result_vector = (int*)aligned_alloc(16, VECTOR_SIZE * sizeof(int));
if (!array1 || !array2 || !result_scalar || !result_vector) {
printf("内存分配失败!\n");
free(array1);
free(array2);
free(result_scalar);
free(result_vector);
return;
}
// 初始化数据
for (int i = 0; i < VECTOR_SIZE; i++) {
array1[i] = i;
array2[i] = i * 2;
result_scalar[i] = 0;
result_vector[i] = 0;
}
printf("数据初始化完成,开始计算...\n");
// 标量版本(传统循环)
for (int i = 0; i < SIZE; i++) {
result_scalar[i] = array1[i] + array2[i];
}
// 向量版本(SSE2)- 使用对齐加载
for (int i = 0; i < SIZE; i += 4) {
__m128i vec1 = _mm_load_si128((__m128i*)(array1 + i));
__m128i vec2 = _mm_load_si128((__m128i*)(array2 + i));
__m128i vec_result = _mm_add_epi32(vec1, vec2);
_mm_store_si128((__m128i*)(result_vector + i), vec_result);
}
// 验证结果
bool correct = true;
for (int i = 0; i < SIZE; i++) {
if (result_scalar[i] != result_vector[i]) {
printf("结果不匹配在索引 %d: 标量=%d, 向量=%d\n",
i, result_scalar[i], result_vector[i]);
correct = false;
break;
}
}
printf("向量化结果验证: %s\n", correct ? "正确" : "错误");
// 显示前几个结果
printf("前8个结果:\n");
printf("索引\t标量\t向量\n");
for (int i = 0; i < 8; i++) {
printf("%d\t%d\t%d\n", i, result_scalar[i], result_vector[i]);
}
// 释放内存
free(array1);
free(array2);
free(result_scalar);
free(result_vector);
}
// 内存对齐示例
void alignment_example() {
printf("\n=== 内存对齐示例 ===\n");
// 对齐的内存(16字节对齐)
alignas(16) int aligned_data[4] = {100, 200, 300, 400};
// 非对齐的内存
int unaligned_data[5] = {1, 2, 3, 4, 5};
int* unaligned_ptr = &unaligned_data[1]; // 故意不对齐,指向的是1号元素
// 对齐加载/存储(更快)
__m128i aligned_vec = _mm_load_si128((__m128i*)aligned_data);
// 非对齐加载/存储(更灵活)
__m128i unaligned_vec = _mm_loadu_si128((__m128i*)unaligned_ptr);
print_m128i_epi32(aligned_vec, "对齐数据");
print_m128i_epi32(unaligned_vec, "非对齐数据");
}
int main() {
printf("SSE2 指令集示例程序\n");
printf("==================\n");
basic_example();
advanced_example();
performance_example();
alignment_example();
return 0;
}