Ampcode Assembly Benchmark

AI-Generated x86-64 SIMD Optimization for Character Counting
Response to Daniel Lemire's "Can your AI rewrite your code in assembly?" — April 2026
CPU
Core Ultra 9 275HX
Architecture
x86-64 / AVX2
Compiler
GCC 15.2.0
AI Agent
Ampcode

Benchmark Results

Implementation ns/string Speedup Performance Bar
Classic C++ (std::count) 67.05 ns 1.00×
46.1%
SSE2 intrinsics (16B) 39.26 ns 1.71×
78.7%
SSE2 4-accumulator (64B) 35.38 ns 1.90×
87.4%
AVX2 intrinsics (32B) 33.97 ns 1.97×
91.0%
AVX2 4-accumulator (128B) 30.91 ns 2.17×
100.0%
AVX2 inline asm 4-acc (128B) 37.30 ns 1.80×
82.9%
AVX2 inline asm 8-acc (256B) 38.00 ns 1.76×
81.3%
AVX2 popcnt (128B) 38.85 ns 1.73×
79.6%

Comparison with Lemire's Results

Lemire's ARM64/NEON Results (Apple M4, Clang)

Lemire tested Grok and Claude on ARM64 NEON. His best result (Claude assembly 3) achieved 154 instructions/string, an ~8× improvement over the classic C++ baseline (1200 instructions/string).

Ampcode x86-64 Results (Intel Arrow Lake, GCC 15.2)

Ampcode generated progressively optimized implementations from SSE2 through AVX2 with multi-accumulator unrolling and inline assembly on x86-64. Best result: 2.17× speedup over the compiler's std::count with -O3 -march=native.

Key techniques used:

Why 2.2× Is Actually the Harder Win

The baselines are not equal

At first glance, Lemire's 8× looks much bigger than our 2.2×. But the numbers tell a different story when you look at what the baselines are actually doing:

Lemire's Baseline

Apple Clang on M4: std::count compiles to a scalar byte-by-byte loop — no SIMD at all.

1200 instructions for ~512-byte avg strings ≈ 2.3 instr/byte: a classic ldrb / cmp / cinc loop. Going from scalar to NEON SIMD is the textbook "just vectorize it" optimization.

Our Baseline (Ampcode)

GCC 15.2 with -O3 -march=native: std::count compiles to AVX2 vectorized code — the compiler already uses SIMD.

We verified this by inspecting the generated assembly — GCC emits vpcmpeqb comparisons in 32-byte chunks. But it sign-extends bytes through vpmovsxbwvpmovsxwdvpmovsxdq to accumulate in 64-bit: ~20 instructions per 32B chunk.

The key difference

Lemire's AIs beat a scalar loop (no SIMD → SIMD). Ampcode beat a SIMD-optimizing compiler's own AVX2 codegen (bad SIMD → better SIMD).

Our approach uses byte-accumulator + vpsadbw reduction: ~4 instructions per 32B chunk vs the compiler's ~20. We didn't just vectorize — we outsmarted the compiler's vectorization strategy.

Lemire Ampcode
What the AI beat A scalar loop (no SIMD) GCC 15.2's own AVX2 codegen
Optimization type "Just vectorize it" Better vectorization strategy
Difficulty Well-known optimization Requires understanding compiler internals