# GCC Vectorization **Vectorization** enables GCC to use SIMD (Single Instruction Multiple Data) instructions—AVX2, AVX-512, NEON, etc.—to process multiple data elements in parallel. A loop that processes one array element per iteration can be transformed to process 4-8 elements simultaneously, massively increasing throughput. ```bash gcc -O3 -march=native -o app app.c # use native CPU's SIMD instructions ``` By default, GCC compiles for a conservative baseline (x86-64) that runs on any machine but doesn't use modern SIMD. `-march=native` tells GCC to use whatever SIMD instructions the build machine has: AVX2 on modern x86, NEON on ARM, etc. Unsafe to redistribute (the binary may crash on older CPUs) but essential for benchmarking and HPC. **Specific target architectures:** ```bash gcc -O3 -march=znver2 -o app app.c # AMD Ryzen (Zen 2) gcc -O3 -march=skylake -o app app.c # Intel Skylake gcc -O3 -march=armv8-a+simd -o app app.c # ARM with NEON ``` Use the exact CPU model if you know it; `-march=native` auto-detects. **Vectorization reports:** Use `-fopt-info-vec-missed` to see which loops GCC successfully vectorized and which it couldn't (and why): ```bash gcc -O3 -march=native -fopt-info-vec-missed hot_loop.c ``` Output shows: "loop vectorized" or "loop not vectorized: reason X". Common blockers: data dependencies, irregular memory access patterns, function calls in the loop. **Data layout matters:** Vectorization is most efficient with simple, regular access patterns: ```c // Vectorizable: unit stride (sequential access) for (int i = 0; i < n; i++) result[i] = a[i] + b[i]; // Not vectorizable: irregular stride for (int i = 0; i < n; i++) result[i] = data[indices[i]]; // indirect access ``` Sequential access (unit stride) is easy to vectorize. Indirect or strided access defeats vectorization. Structure-of-arrays is often more vectorizable than array-of-structures. **Explicit SIMD intrinsics:** If GCC can't auto-vectorize, write SIMD code manually using intrinsics: ```c #include __m256 a = _mm256_loadu_ps(data); // load 8 floats __m256 b = _mm256_loadu_ps(data+8); __m256 c = _mm256_add_ps(a, b); // add all 8 pairs _mm256_storeu_ps(result, c); ``` This gives fine-grained control but is verbose. Use only when auto-vectorization fails and you've measured that it matters. **Compiler pragmas:** Hint to the compiler that a loop is vectorizable: ```c #pragma omp simd for (int i = 0; i < n; i++) result[i] = a[i] * b[i]; ``` `#pragma omp simd` tells GCC "this loop has no dependencies, please vectorize it aggressively." Use when you know the loop is safe but the compiler's analysis is conservative.