Intel C++ Compiler (ICX/ICPX)
Intel's oneAPI C++ compiler — SIMD auto-vectorization, IPO, PGO, OpenMP, AVX-512, and drop-in GCC/Clang compatibility.
TL;DR
ICX is Intel's LLVM-based C++ compiler and the fastest way to extract peak performance from Intel CPUs. It is the direct successor to the legacy ICC (Intel C++ Compiler Classic), rebuilt on LLVM/Clang infrastructure. Where GCC and Clang produce good vectorized code, ICX produces better vectorized code on Intel silicon — especially for AVX-512-capable processors like Skylake-X, Ice Lake, and Sapphire Rapids.
Use ICX when you need:
- Maximum single-core throughput on Intel CPUs (HPC, scientific computing, numerical simulation)
- Best-in-class AVX-512 auto-vectorization without hand-writing intrinsics
- IPO/PGO without build-system surgery
- OpenMP 5.1 offload to Intel GPUs (via SYCL/DPC++)
# Install environment (after Intel oneAPI Base Toolkit install)
source /opt/intel/oneapi/setvars.sh
# Drop-in replacement for g++
icpx -std=c++20 -O2 -Wall -march=native -o myapp main.cpp
# Check version
icpx --versionICX vs ICC vs GCC vs Clang
ICX is not ICC. ICC (the classic Intel compiler) used Intel's own proprietary backend and is now in maintenance mode. ICX uses the LLVM/Clang frontend and middle-end, with Intel-specific optimizations layered on top — which means you get Clang's diagnostics and standards conformance plus Intel's vectorizer.
| Feature | ICX (oneAPI) | ICC (Classic) | GCC 14 | Clang 19 |
|---|---|---|---|---|
| Backend | LLVM | Proprietary | GCC | LLVM |
| C++23 support | Strong (Clang-based) | Weak (legacy) | Partial | Strong |
| C++20 modules | Yes (Clang-based) | No | Partial | Yes |
| AVX-512 auto-vec quality | Excellent | Excellent | Good | Good |
| OpenMP version | 5.1 | 4.5 | 5.1 | 5.0 |
| OpenMP offload (GPU) | Yes (SYCL) | Limited | No | Partial |
| License | Free (oneAPI) | Free (legacy) | GPLv3 | Apache 2 |
| Linux support | Yes | Yes | Yes | Yes |
| Windows support | Yes | Yes | No | Yes |
| macOS support | No | Limited | Yes | Yes |
| Clang flags compatible | Yes | Partially | No | Yes |
| GCC flags compatible | Yes | Partially | Yes | Mostly |
The key takeaway: ICX gives you Clang-level standards conformance with Intel-level vectorization. There is rarely a reason to use ICC in new projects.
Installation
Intel oneAPI Base Toolkit is free and contains ICX, the Math Kernel Library (MKL), VTune, and other tools. You do not need to create an account for the apt/dnf package manager path.
Ubuntu / Debian
# Add Intel oneAPI repository
wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \
| gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] \
https://apt.repos.intel.com/oneapi all main" \
| sudo tee /etc/apt/sources.list.d/oneAPI.list
sudo apt update
# Full Base Toolkit (~7 GB, includes VTune, MKL, etc.)
sudo apt install intel-basekit
# Minimal — compiler only (~1 GB)
sudo apt install intel-oneapi-compiler-dpcpp-cppRHEL / Fedora / openSUSE
# Add repo
sudo tee /etc/yum.repos.d/oneAPI.repo << 'EOF'
[oneAPI]
name=Intel® oneAPI repository
baseurl=https://yum.repos.intel.com/oneapi
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://yum.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
EOF
sudo dnf install intel-basekitActivating the Environment
ICX requires environment variables for library paths and license discovery. Source the setup script before use:
# Activate all oneAPI components for current shell session
source /opt/intel/oneapi/setvars.sh
# Or activate just the compiler (faster)
source /opt/intel/oneapi/compiler/latest/env/vars.sh
# Verify
which icpx # /opt/intel/oneapi/compiler/latest/bin/icpx
icpx --versionAdd the source line to your ~/.bashrc or ~/.zshrc if you use ICX regularly, or wrap it in a project shell script.
Docker
# Intel provides official base images
docker pull intel/oneapi-basekit:latest
# Or use as a build stage
FROM intel/oneapi-basekit:2025.0-devel-ubuntu22.04 AS build
RUN source /opt/intel/oneapi/setvars.sh && icpx -O2 -std=c++20 main.cpp -o appWindows
Download the Intel oneAPI Base Toolkit installer from intel.com/oneapi. The installer adds icx.exe and icpx.exe to your PATH and integrates with Visual Studio 2019/2022. Use the Intel oneAPI command prompt shortcut for a pre-activated environment.
Drop-In GCC/Clang Compatibility
ICX is designed to replace GCC and Clang with zero code changes. The two binaries map directly to the conventional C/C++ compilers:
| oneAPI binary | Replaces |
|---|---|
icx | gcc / clang (C compiler) |
icpx | g++ / clang++ (C++ compiler) |
All standard flags work identically:
# These work exactly as with GCC/Clang
icpx -std=c++20 -O2 -Wall -Wextra -Wpedantic -g -fPIC main.cpp -o app
# Link against system libraries the same way
icpx -std=c++20 -O2 main.cpp -lpthread -lm -o app
# Compile-only (produce .o files)
icpx -std=c++20 -O2 -c foo.cpp -o foo.o
icpx -std=c++20 -O2 -c bar.cpp -o bar.o
icpx foo.o bar.o -o appCMake Integration
Switching a CMake project to ICX is a one-liner:
source /opt/intel/oneapi/setvars.sh
cmake -B build \
-DCMAKE_C_COMPILER=icx \
-DCMAKE_CXX_COMPILER=icpx \
-DCMAKE_BUILD_TYPE=Release \
.
cmake --build build -j$(nproc)For a reusable toolchain file:
# toolchains/intel-icx.cmake
set(CMAKE_C_COMPILER icx)
set(CMAKE_CXX_COMPILER icpx)
# Intel-specific flags only when using ICX
if(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
add_compile_options(-xHost -ipo)
endif()cmake -B build -DCMAKE_TOOLCHAIN_FILE=toolchains/intel-icx.cmake .CMake 3.20+ recognises IntelLLVM as a compiler ID, so you can use generator expressions to add ICX-specific flags without breaking other compilers:
target_compile_options(mylib PRIVATE
$<$<CXX_COMPILER_ID:IntelLLVM>:-xHost -qopt-report=2>
$<$<CXX_COMPILER_ID:GNU>:-march=native>
$<$<CXX_COMPILER_ID:Clang>:-march=native>
)C++ Standards Support
ICX is built on the Clang frontend, so its standards conformance tracks Clang closely. This is a significant upgrade over legacy ICC, which had spotty C++17 support and essentially no C++20.
| Standard | ICX Status | Clang 19 | GCC 14 |
|---|---|---|---|
| C++11 | Full | Full | Full |
| C++14 | Full | Full | Full |
| C++17 | Full | Full | Full |
| C++20 | Full | Full | Full |
| C++23 | Partial (most features) | Partial | Partial |
| C++26 | Early/experimental | Early | Early |
# Use standard flags exactly as with Clang
icpx -std=c++20 main.cpp -o app
icpx -std=c++23 main.cpp -o app
# Check what's enabled
icpx -std=c++20 -E -dM /dev/null | grep __cplusplus
# Output: #define __cplusplus 202002LModule support (C++20) is present and improving. For production use, C++17 and C++20 are fully reliable; C++23 features should be tested case by case.
Auto-Vectorization — ICX's Core Strength
Auto-vectorization is where ICX earns its place. At -O2, ICX enables aggressive auto-vectorization. It analyses loops, identifies reduction patterns, identifies scatter/gather opportunities, and emits wide SIMD instructions — often better than GCC or Clang for Intel-specific microarchitectures.
Enabling and Inspecting Vectorization
# -O2 enables auto-vectorization by default
icpx -O2 -std=c++20 hot_loop.cpp -o hot_loop
# Generate a vectorization report (levels 1–5, 5 is most verbose)
icpx -O2 -qopt-report=5 -qopt-report-phase=vec hot_loop.cpp -o hot_loop
# Report is written to hot_loop.optrptReading the Vectorization Report
Given this simple loop:
// hot_loop.cpp
void scale(float* __restrict__ out, const float* __restrict__ in,
float factor, int n) {
for (int i = 0; i < n; ++i)
out[i] = in[i] * factor;
}The report (hot_loop.optrpt) will contain something like:
LOOP BEGIN at hot_loop.cpp(4,5)
remark #15300: LOOP WAS VECTORIZED
remark #15475: --- begin vector loop cost summary ---
remark #15476: scalar loop cost: 4
remark #15477: vector loop cost: 0.25
remark #15478: estimated potential speedup: 15.00
remark #15488: --- end vector loop cost summary ---
remark #25015: Estimate of max trip count of loop=31
LOOP ENDAnd for a loop that failed to vectorize:
LOOP BEGIN at hot_loop.cpp(12,5)
remark #15344: loop was not vectorized: vector dependence prevents vectorization
remark #15346: vector dependence: assumed FLOW dependence between out[i] (line 13) and in[i] (line 13)
LOOP ENDDependence is the most common failure reason. If you know there is no aliasing, tell the compiler.
Writing Vectorization-Friendly Code
ICX can vectorize loops automatically, but you can help it significantly:
1. Use __restrict__ to eliminate aliasing ambiguity:
// ICX assumes out and in may alias — can't vectorize safely
void bad(float* out, const float* in, int n);
// __restrict__ tells ICX: these pointers never overlap
void good(float* __restrict__ out, const float* __restrict__ in, int n);2. Use #pragma ivdep when you know there is no loop-carried dependence:
void update(float* a, const int* idx, float v, int n) {
#pragma ivdep
for (int i = 0; i < n; ++i)
a[idx[i]] += v; // ICX now trusts no idx[i] values repeat
}3. Use #pragma omp simd for explicit SIMD (more portable than ivdep):
void saxpy(float* __restrict__ y, const float* __restrict__ x,
float a, int n) {
#pragma omp simd
for (int i = 0; i < n; ++i)
y[i] = a * x[i] + y[i];
}4. Keep loops simple and strip-mineable:
// Hard to vectorize: early exit, function calls, complex control flow
for (int i = 0; i < n; ++i) {
if (complicated_condition(i)) break;
result[i] = some_function(data[i]);
}
// Easy to vectorize: linear access, no early exits, simple body
for (int i = 0; i < n; ++i)
result[i] = data[i] * 2.0f + bias[i];5. Ensure alignment for wider loads: Align heap buffers to at least 64 bytes for AVX-512:
#include <cstdlib>
float* buf = static_cast<float*>(std::aligned_alloc(64, n * sizeof(float)));
// Or use Intel's aligned allocator
#include <mkl.h>
float* buf = (float*)mkl_malloc(n * sizeof(float), 64);AVX-512 Targeting
Sapphire Rapids and Ice Lake server CPUs support AVX-512, and ICX knows how to use every extension. AVX-512 doubles the SIMD register width (512 bits = 16 floats or 8 doubles per register) and adds masking, scatter/gather, and conflict detection instructions.
Detect What Your CPU Supports
# Check for AVX-512 feature flags
grep -oE 'avx512[a-z]+' /proc/cpuinfo | sort -u
# Common feature flags:
# avx512f — Foundation (required for any AVX-512)
# avx512bw — Byte and Word operations
# avx512dq — Doubleword and Quadword operations
# avx512vl — Vector Length extensions (AVX-512 on 128/256-bit registers)
# avx512vnni — Vector Neural Network Instructions
# avx512amx — Advanced Matrix Extensions (Sapphire Rapids)Targeting Flags
# Auto-detect and use all features of the current CPU (best for local builds)
icpx -O2 -xHost main.cpp -o app
# Target a specific microarchitecture
icpx -O2 -march=icelake-server main.cpp -o app # Ice Lake Xeon
icpx -O2 -march=sapphirerapids main.cpp -o app # Sapphire Rapids
icpx -O2 -march=skylake-avx512 main.cpp -o app # Skylake-X
# Portable multi-target dispatch: generate a baseline + AVX-512 code path
# Runtime dispatches to best available on end-user CPU
icpx -O2 -axAVX512F,AVX512BW,AVX512DQ main.cpp -o app
# -ax generates multiple code paths; baseline is still SSE4.2
# Use -x for a hard minimum (binary won't run on older CPUs):
icpx -O2 -xAVX512F main.cpp -o appThe -axCORE-AVX512 flag is the common shorthand that covers the most useful AVX-512 subsets:
icpx -O2 -axCORE-AVX512 main.cpp -o appManual AVX-512 Intrinsics
When the auto-vectorizer leaves performance on the table, drop down to intrinsics. ICX fully supports all _mm512_* intrinsics:
#include <immintrin.h>
// Multiply-add 16 floats per iteration using AVX-512
void fma_avx512(float* __restrict__ c,
const float* __restrict__ a,
const float* __restrict__ b,
float scalar, int n) {
__m512 vs = _mm512_set1_ps(scalar);
int i = 0;
for (; i + 16 <= n; i += 16) {
__m512 va = _mm512_loadu_ps(a + i);
__m512 vb = _mm512_loadu_ps(b + i);
__m512 vc = _mm512_fmadd_ps(va, vs, vb); // c[i] = a[i]*scalar + b[i]
_mm512_storeu_ps(c + i, vc);
}
// Scalar tail
for (; i < n; ++i)
c[i] = a[i] * scalar + b[i];
}Prefer aligned loads (_mm512_load_ps) over unaligned (_mm512_loadu_ps) when you can guarantee 64-byte alignment — it avoids a potential split-cache-line penalty on older microarchitectures.
Interprocedural Optimization (IPO)
-ipo enables whole-program analysis across translation units — equivalent to LTO in GCC/Clang, but with Intel's optimizer making cross-file inlining and devirtualization decisions. This is particularly valuable for C++ code that passes small objects across module boundaries.
Basic IPO
# Enable IPO — ICX compiles all listed .cpp files together
icpx -O2 -ipo -std=c++20 foo.cpp bar.cpp baz.cpp -o app
# IPO with object files (deferred IPO, faster incremental)
icpx -O2 -ipo -c foo.cpp -o foo.o
icpx -O2 -ipo -c bar.cpp -o bar.o
icpx -O2 -ipo foo.o bar.o -o appWhen using -ipo with separate compilation, ICX writes LLVM bitcode into the .o files instead of native code, then performs full link-time optimization during the final link step.
IPO Wrappers for Static Libraries
IPO requires Intel's own archiver and linker wrappers when building static libraries:
# Use xiar instead of ar, xild instead of ld
xiar rcs libmylib.a foo.o bar.o
icpx -O2 -ipo main.o -L. -lmylib -o app
# Or let ICX drive the link (it auto-selects xild)
icpx -O2 -ipo -static main.o foo.o bar.o -o appCMake with IPO
CMake's INTERPROCEDURAL_OPTIMIZATION property works with ICX:
cmake_minimum_required(VERSION 3.9)
project(myproject CXX)
# Enable IPO globally
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
add_executable(myapp main.cpp foo.cpp bar.cpp)
target_compile_options(myapp PRIVATE -O2)CMake automatically passes the correct -ipo flags and selects xiar/xild when ICX is the compiler.
Profile-Guided Optimization (PGO)
PGO can deliver 10–30% speed improvements on branchy, real-world code by training the compiler on actual runtime behaviour. ICX's PGO is a three-step process: instrument → profile → optimize.
Step 1: Instrument Build
# Compile with profiling instrumentation
icpx -O2 -prof-gen -std=c++20 main.cpp foo.cpp bar.cpp -o app_instrumented
# Optional: specify where profile data goes
icpx -O2 -prof-gen -prof-dir=/tmp/pgo-profiles \
main.cpp foo.cpp bar.cpp -o app_instrumentedStep 2: Run Your Workload
# Run the instrumented binary with representative input(s)
./app_instrumented --input dataset1.bin
./app_instrumented --input dataset2.bin
./app_instrumented --input dataset3.bin
# Profile data (.profraw / pgopti.dyn) is written to the current directory
# or PROF_DIR if setFor best results, use inputs that represent real production workloads. PGO optimizes for whatever branch patterns and call frequencies you exercise here.
Step 3: Optimized Build
# Rebuild using the collected profiles
icpx -O2 -prof-use -std=c++20 main.cpp foo.cpp bar.cpp -o app_optimized
# Specify profile directory if non-default
icpx -O2 -prof-use -prof-dir=/tmp/pgo-profiles \
main.cpp foo.cpp bar.cpp -o app_optimizedThe compiler merges all .dyn files found in the profile directory and uses them to:
- Reorder basic blocks to improve icache density
- Inline frequently-called functions that would otherwise be over size limit
- Predict branches correctly, enabling better instruction scheduling
- Devirtualize virtual calls when the profiled type is always the same
PGO + IPO Together
Combining IPO and PGO gives the best results for large programs:
# Step 1: instrument with IPO
icpx -O2 -ipo -prof-gen main.cpp foo.cpp bar.cpp -o app_inst
# Step 2: run
./app_inst --input representative_data
# Step 3: rebuild with both
icpx -O2 -ipo -prof-use main.cpp foo.cpp bar.cpp -o app_finalOpenMP and Parallelism
ICX ships with one of the most complete OpenMP 5.1 implementations available. Enable OpenMP with -qopenmp; link with the same flag so the runtime is pulled in.
Basic Parallel Loop
icpx -O2 -qopenmp -std=c++20 -o parallel_app main.cpp#include <omp.h>
#include <vector>
#include <cmath>
void process(std::vector<float>& data) {
#pragma omp parallel for schedule(static)
for (int i = 0; i < (int)data.size(); ++i)
data[i] = std::sqrt(data[i]) + std::log(data[i] + 1.0f);
}Parallel + SIMD (Outer threading, inner vectorization)
Combining OpenMP parallel with SIMD directives is where ICX really shines — it can thread across cores and vectorize each thread's inner loop:
void dot_product_parallel(const float* __restrict__ a,
const float* __restrict__ b,
float* result, int n) {
float sum = 0.0f;
#pragma omp parallel for simd reduction(+:sum) schedule(static)
for (int i = 0; i < n; ++i)
sum += a[i] * b[i];
*result = sum;
}Task-Based Parallelism
#include <omp.h>
void merge_sort(int* arr, int lo, int hi) {
if (hi - lo < 1024) {
insertion_sort(arr, lo, hi);
return;
}
int mid = lo + (hi - lo) / 2;
#pragma omp task
merge_sort(arr, lo, mid);
#pragma omp task
merge_sort(arr, mid + 1, hi);
#pragma omp taskwait
merge(arr, lo, mid, hi);
}
int main() {
int arr[65536] = { /* ... */ };
#pragma omp parallel
#pragma omp single
merge_sort(arr, 0, 65535);
}Controlling Thread Count
# Environment variable (standard OpenMP)
OMP_NUM_THREADS=8 ./app
# Or in code
omp_set_num_threads(8);
printf("Using %d threads\n", omp_get_max_threads());OpenMP 5.1 Features in ICX
ICX supports OpenMP 5.1 features including:
// Loop transformations (OpenMP 5.1)
#pragma omp tile sizes(4,4)
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
C[i][j] += A[i][k] * B[k][j];
// Declare variant for target-specific versions
#pragma omp declare variant(fast_sqrt_avx) match(device={isa(avx512f)})
float my_sqrt(float x);CMake Integration
Basic Setup
source /opt/intel/oneapi/setvars.sh
cmake -B build \
-DCMAKE_C_COMPILER=icx \
-DCMAKE_CXX_COMPILER=icpx \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
.
cmake --build build -j$(nproc)Toolchain File
# toolchains/intel-icx.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER icx)
set(CMAKE_CXX_COMPILER icpx)
# Ensure linker wrappers are used for IPO
set(CMAKE_AR xiar)
set(CMAKE_LINKER xild)Generator Expressions for ICX-Specific Flags
Use generator expressions to apply Intel-only flags without breaking GCC/Clang builds:
add_library(numerics STATIC numerics.cpp)
target_compile_options(numerics PRIVATE
-O2
-std=c++20
$<$<CXX_COMPILER_ID:IntelLLVM>:
-xHost
-qopt-report=2
-fp-model fast=2
-qopenmp
>
$<$<CXX_COMPILER_ID:GNU>:
-march=native
-fopenmp
>
)
target_link_options(numerics PRIVATE
$<$<CXX_COMPILER_ID:IntelLLVM>:-qopenmp>
$<$<CXX_COMPILER_ID:GNU>:-fopenmp>
)Finding Intel MKL from CMake
ICX ships alongside MKL. Use the oneAPI CMake config files to link against it:
# Requires: source /opt/intel/oneapi/setvars.sh first
find_package(MKL CONFIG REQUIRED PATHS "$ENV{MKLROOT}/lib/cmake/mkl")
target_link_libraries(myapp PRIVATE MKL::MKL)Diagnostic and Warning Flags
ICX inherits Clang's excellent diagnostics and adds Intel-specific vectorization reporting on top.
Standard Warning Flags
# Same as Clang — these all work
icpx -Wall -Wextra -Wpedantic main.cpp
# Stricter (catch more real bugs)
icpx -Wall -Wextra -Wpedantic \
-Wshadow -Wconversion -Wsign-conversion \
-Wnull-dereference -Wdouble-promotion \
main.cpp
# Treat warnings as errors
icpx -Wall -Wextra -Werror main.cppIntel-Specific Diagnostic Flags
# Enable ALL warnings (more than -Wall, includes vectorization and performance hints)
icpx -Wcheck main.cpp
# Enable vectorization diagnostic messages inline (requires -O2)
icpx -O2 -diag-enable vec main.cpp
# Vectorization report to file (levels 1–5)
icpx -O2 -qopt-report=3 main.cpp
# Suppress a specific Intel diagnostic number
icpx -diag-disable 15552 main.cpp
# Show which diagnostic number an remark belongs to
icpx -O2 -qopt-report=5 -qopt-report-stdout main.cppFloating-Point Model Flags
Floating-point handling is a critical correctness-vs-speed trade-off in HPC code:
# Strict IEEE 754 compliance — no reordering, no fused ops
icpx -fp-model precise main.cpp
# Allow reassociation and fused multiply-add (safe for most HPC)
icpx -fp-model fast=1 main.cpp
# Maximum speed — may change results slightly (auto-vectorization + SVML)
icpx -fp-model fast=2 main.cpp
# Default is fast=1 at -O2. Explicitly use 'precise' if results matter more than speed.Runtime Diagnostics
# Add stack traceback on crash (prints file:line for signal handlers)
icpx -traceback main.cpp
# Bounds checking (debug builds only — major slowdown)
icpx -O0 -check=bounds main.cpp
# Check for stack corruption
icpx -O0 -check=stack main.cppLicensing and Free Tier
Intel oneAPI is completely free. There is no paid tier for the C++ compiler, no license server, no registration requirement when installing via the package manager. The toolkit includes:
icx/icpx— C and C++ compilersifx— Fortran compiler- Intel MKL — optimized BLAS/LAPACK
- Intel TBB — Threading Building Blocks
- Intel VTune Profiler
- Intel Inspector (memory/threading error detection)
- Intel Advisor (vectorization and offload advisor)
# All free, no license check
sudo apt install intel-basekit
# Or install individual components
sudo apt install intel-oneapi-compiler-dpcpp-cpp # compiler only
sudo apt install intel-oneapi-mkl # MKL only
sudo apt install intel-oneapi-vtune # VTune onlyThe only restriction is that Intel oneAPI is not open source — you cannot redistribute modified versions of the compiler itself. For commercial use in your own projects (building and distributing your compiled binaries), there are no restrictions.
See Also
- GCC — GNU compiler, widest architecture support, go-to for non-Intel targets
- Clang — LLVM compiler, best diagnostics, powers clang-tidy and clangd
- MSVC — Microsoft's compiler, required for Windows system integration
- SIMD Intrinsics — Hand-written AVX/AVX-512 intrinsics when auto-vectorization isn't enough