mcpField
S
simd-intrinsics

simd-intrinsics

github.com/mohitmishra786开发269656 次安装18 周前更新
代码仓库

关于 simd-intrinsics

为 Agent 提供 SIMD 编程指导,涵盖自动向量化分析、SSE2、AVX2 和 NEON 内建函数,以及运行时 CPU 特性检测。帮助开发者判断何时依赖编译器,何时编写手动向量指令。

主要功能

  1. 01检查编译器自动向量化
  2. 02SSE2、AVX2 与 NEON 内建函数
  3. 03运行时 CPU 特性检测
  4. 04处理自动向量化障碍
  5. 05比较自动向量化与手写指令
  6. 06对齐和性能提示

适用场景

  1. 01检查循环是否已自动向量化
  2. 02为 x86 或 ARM 编写 SIMD 代码
  3. 03按 CPU 特性选择处理路径
  4. 04决定是否使用 SIMD 内建函数

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill simd-intrinsics

安装后在 Claude Code 中输入/simd-intrinsics确认已加载。

附带文件references/intel-intrinsics-guide.md

详细介绍

SIMD 内建函数

用途

指导 Agent 使用 SIMD:查看自动向量化输出、编写 SSE2/AVX2/NEON 内建函数、运行时检测 CPU 特性,以及在编译器自动向量化和手动内建函数之间做出选择。

触发条件

  • “如何检查我的循环是否正在进行自动向量化?”
  • “如何编写 SSE2/AVX2 内建函数?”
  • “自动向量化失败了——如何修复?”
  • “如何在运行时检查 CPU 特性?”
  • “应该使用内建函数,还是让编译器进行向量化?”
  • “如何为 ARM 编写 NEON 内建函数?”

工作流

1. 检查自动向量化
# GCC: show vectorization info
gcc -O2 -march=native -fopt-info-vec src/hot.c -o hot

# Verbose: show missed + successful
gcc -O2 -march=native -fopt-info-vec-missed -fopt-info-vec-optimized src/hot.c

# Clang: vectorization remarks
clang -O2 -march=native \
    -Rpass=loop-vectorize \
    -Rpass-missed=loop-vectorize \
    -Rpass-analysis=loop-vectorize \
    src/hot.c -o hot

# Example missed message:
# hot.c:15:5: remark: loop not vectorized: value that could not be identified as
# reduction is used outside the loop [-Rpass-missed=loop-vectorize]

自动向量化的常见阻碍:

阻碍修复方法
循环携带依赖重构代码以消除依赖
依赖数据的退出条件(提前返回)将退出条件移到循环之后
内存不连续使用 gather/scatter 或进行重构
别名问题(指针可能存在别名)添加 __restrict__ 或 restrict
循环次数未知添加 __builtin_expect 或提示
循环体中调用函数内联该函数
// Help the compiler by adding restrict
void add_arrays(float * __restrict__ dst,
                const float * __restrict__ a,
                const float * __restrict__ b,
                size_t n) {
    for (size_t i = 0; i < n; i++)
        dst[i] = a[i] + b[i];  // Now vectorizable
}
2. 运行时 CPU 特性检测
// Linux: use __builtin_cpu_supports (GCC/Clang)
if (__builtin_cpu_supports("avx2")) {
    process_avx2(data, len);
} else if (__builtin_cpu_supports("sse4.2")) {
    process_sse42(data, len);
} else {
    process_scalar(data, len);
}

// Check specific features:
__builtin_cpu_supports("sse2")
__builtin_cpu_supports("sse4.1")
__builtin_cpu_supports("sse4.2")
__builtin_cpu_supports("avx")
__builtin_cpu_supports("avx2")
__builtin_cpu_supports("avx512f")
__builtin_cpu_supports("bmi")
__builtin_cpu_supports("bmi2")
__builtin_cpu_supports("fma")
// Portable: use CPUID directly
#include <cpuid.h>

static int has_avx2(void) {
    unsigned int eax, ebx, ecx, edx;
    // CPUID leaf 7, subleaf 0
    __cpuid_count(7, 0, eax, ebx, ecx, edx);
    return (ebx >> 5) & 1;  // bit 5 = AVX2
}
3. SSE2 / SSE4.2 内建函数(x86)
#include <immintrin.h>  // All x86 intrinsics

// SSE2: 128-bit vectors
// __m128  = 4 floats
// __m128d = 2 doubles
// __m128i = integers (8x16, 4x32, 2x64, 16x8)

void sum_floats_sse2(float *dst, const float *a, const float *b, int n) {
    int i = 0;
    for (; i <= n - 4; i += 4) {
        __m128 va = _mm_loadu_ps(a + i);  // unaligned load
        __m128 vb = _mm_loadu_ps(b + i);
        __m128 vc = _mm_add_ps(va, vb);
        _mm_storeu_ps(dst + i, vc);       // unaligned store
    }
    // Handle remainder
    for (; i < n; i++) dst[i] = a[i] + b[i];
}
4. AVX2 内建函数(x86)
#ifdef __AVX2__
#include <immintrin.h>

// __m256  = 8 floats, __m256d = 4 doubles, __m256i = integers

void sum_floats_avx2(float *dst, const float *a, const float *b, int n) {
    int i = 0;
    for (; i <= n - 8; i += 8) {
        __m256 va = _mm256_loadu_ps(a + i);
        __m256 vb = _mm256_loadu_ps(b + i);
        __m256 vc = _mm256_add_ps(va, vb);
        _mm256_storeu_ps(dst + i, vc);
    }
    // SSE2 tail (4 elements)
    for (; i <= n - 4; i += 4) {
        __m128 va = _mm_loadu_ps(a + i);
        __m128 vb = _mm_loadu_ps(b + i);
        _mm_storeu_ps(dst + i, _mm_add_ps(va, vb));
    }
    // Scalar tail
    for (; i < n; i++) dst[i] = a[i] + b[i];
}

// Fused multiply-add (FMA) — 1 instruction for a*b+c
void fma_avx2(float *dst, const float *a, const float *b, const float *c, int n) {
    for (int i = 0; i <= n - 8; i += 8) {
        __m256 va = _mm256_loadu_ps(a + i);
        __m256 vb = _mm256_loadu_ps(b + i);
        __m256 vc = _mm256_loadu_ps(c + i);
        _mm256_storeu_ps(dst + i, _mm256_fmadd_ps(va, vb, vc)); // dst = a*b + c
    }
}
#endif

使用以下命令编译:gcc -O2 -mavx2 -mfma src/simd.c

5. NEON 内建函数(ARM/AArch64)
#include <arm_neon.h>

// float32x4_t = 4 floats (128-bit)
// float32x8_t = 8 floats (ARM SVE — scalable)
// uint8x16_t  = 16 bytes
// int32x4_t   = 4 int32

void sum_floats_neon(float *dst, const float *a, const float *b, int n) {
    int i = 0;
    for (; i <= n - 4; i += 4) {
        float32x4_t va = vld1q_f32(a + i);  // load 4 floats
        float32x4_t vb = vld1q_f32(b + i);
        float32x4_t vc = vaddq_f32(va, vb);  // add
        vst1q_f32(dst + i, vc);               // store 4 floats
    }
    for (; i < n; i++) dst[i] = a[i] + b[i];
}

// AArch64 FMA
void fma_neon(float *dst, const float *a, const float *b, const float *c, int n) {
    for (int i = 0; i <= n - 4; i += 4) {
        float32x4_t va = vld1q_f32(a + i);
        float32x4_t vb = vld1q_f32(b + i);
        float32x4_t vc = vld1q_f32(c + i);
        vst1q_f32(dst + i, vfmaq_f32(vc, va, vb));  // vc + va*vb
    }
}

使用以下命令编译:gcc -O2 -march=armv8-a+simd src/simd.c

6. 选择自动向量化还是内建函数
Can the compiler auto-vectorize?
  → Try first: add __restrict__, remove complex control flow, align data
  → Check with -fopt-info-vec or -Rpass=loop-vectorize
  → If vectorized: verify correctness and performance

Still need intrinsics?
  → Prefer compiler builtins: __builtin_popcount, __builtin_ctz
  → Use SIMD intrinsics for: hand-tuned shuffles, gather/scatter, horizontal ops
  → Avoid intrinsics for: simple element-wise ops (let compiler do it)
7. 对齐与性能
// Aligned allocation (required for _mm256_load_ps, optional for _mm256_loadu_ps)
float *buf = (float *)aligned_alloc(32, n * sizeof(float));
// 32-byte alignment for AVX2, 64 for AVX-512

// Hint alignment to compiler
float *__attribute__((aligned(32))) buf = ...;

// Use aligned loads when data is aligned (faster)
__m256 v = _mm256_load_ps(aligned_ptr);    // requires 32-byte alignment
__m256 v = _mm256_loadu_ps(unaligned_ptr); // any alignment, slightly slower on old CPUs

Intel Intrinsics Guide 参考资料和 NEON 查找表,请参见 references/intel-intrinsics-guide.md。

相关技能

  • 使用 skills/compilers/gcc 设置 -march、-msse4.2、-mavx2 标志
  • 使用 skills/compilers/clang 查看向量化提示并控制自动向量化
  • 使用 skills/profilers/linux-perf 通过 perf stat 计数器测量 SIMD 的影响
  • 使用 skills/low-level-programming/assembly-x86 阅读 SIMD 汇编输出

评论

支持 Markdown