001 Notes
miniz Optimization: Checksums Win Before the Codec Does
0. Abstract
the checksum gets much faster first
the full codec improves only where that checksum or a safe copy path is visible
| Area | Best useful result |
|---|---|
| CRC-32 on Ryzen | 5.34x with portable slice-by-8 |
| Adler-32 on Ryzen | 4.46x with AVX2 weighted sums |
| Compression on 64 MiB text | 1.17x |
| Decompression on 64 MiB text | 2.12x |
| AVX2 match compare microbench | 4.51x |
| Custom AVX2 copy | slower than libc memcpy |
| Simple prefetch chain walk | slower than no prefetch |
SIMD wins when the operation is wide, regular, and semantically correct.
It does not rescue serial parsing, the wrong polynomial, bad prefetch distance,
or a worse memcpy than libc already ships.
1. Test Platform
| Item | Value |
|---|---|
| CPU | AMD Ryzen 9 5900HX with Radeon Graphics |
| Architecture | x86_64 |
| OS | Linux 7.0.0-22-generic |
| Compiler | GCC 15.2.0 |
| Relevant CPU features | SSE4.2, SSSE3, AVX2, PCLMULQDQ, VPCLMULQDQ |
| Raw rows | 192 benchmark rows |
2. What Was Optimized
| Group | Baseline | Candidate optimization |
|---|---|---|
| CRC-32 | byte/table CRC-32 | slice-by-8 CRC-32 |
| Adler-32 | unrolled scalar Adler-32 | scalar 16x, SSSE3 weighted sums, AVX2 weighted sums |
| Compression | level-6 codec path | prefetch hints and AVX2 long-match comparison |
| Decompression | codec path with checksum/copy work | optimized Adler updates and guarded 64-byte AVX2 copy |
x86 SSE4.2 CRC instructions compute CRC32C.
zlib/miniz CRC-32 is a different polynomial.
Fast and wrong is still wrong.
CRC-32 slice-by-8
static uint32_t crc32_slice_by_8(const uint8_t* p, size_t n, uint32_t crc) {
crc = ~crc;
while (n >= 8) {
uint32_t lo;
uint32_t hi;
memcpy(&lo, p, sizeof(lo));
memcpy(&hi, p + 4, sizeof(hi));
crc ^= lo;
crc =
crc_table[7][crc & 0xff] ^
crc_table[6][(crc >> 8) & 0xff] ^
crc_table[5][(crc >> 16) & 0xff] ^
crc_table[4][crc >> 24] ^
crc_table[3][hi & 0xff] ^
crc_table[2][(hi >> 8) & 0xff] ^
crc_table[1][(hi >> 16) & 0xff] ^
crc_table[0][hi >> 24];
p += 8;
n -= 8;
}
while (n-- != 0) {
crc = crc_table[0][(crc ^ *p++) & 0xff] ^ (crc >> 8);
}
return ~crc;
}
static uint32_t crc32c_not_zlib_crc32(const uint8_t* p, size_t n) {
uint64_t crc = 0xffffffffu;
while (n >= 8) {
uint64_t word;
memcpy(&word, p, sizeof(word));
crc = _mm_crc32_u64(crc, word);
p += 8;
n -= 8;
}
while (n-- != 0) {
crc = _mm_crc32_u8(static_cast<uint32_t>(crc), *p++);
}
return static_cast<uint32_t>(~crc);
}
Adler-32 weighted sums
static inline void adler32_avx2_chunk32(
const uint8_t* p,
uint32_t* s1,
uint32_t* s2) {
const __m256i bytes = _mm256_loadu_si256(
reinterpret_cast<const __m256i*>(p));
const __m256i zero = _mm256_setzero_si256();
const __m256i weights = _mm256_setr_epi8(
32, 31, 30, 29, 28, 27, 26, 25,
24, 23, 22, 21, 20, 19, 18, 17,
16, 15, 14, 13, 12, 11, 10, 9,
8, 7, 6, 5, 4, 3, 2, 1);
const __m256i ones = _mm256_set1_epi16(1);
const uint32_t byte_sum =
horizontal_sum_epu64(_mm256_sad_epu8(bytes, zero));
const __m256i pair_weighted = _mm256_maddubs_epi16(bytes, weights);
const uint32_t weighted_sum = horizontal_sum_epi32(
_mm256_madd_epi16(pair_weighted, ones));
*s2 += 32 * *s1 + weighted_sum;
*s1 += byte_sum;
*s1 %= 65521;
*s2 %= 65521;
}
3. Checksum Results
| Algorithm | Variant | Size | MB/s | Median ms | Correct |
|---|---|---|---|---|---|
| crc32 | miniz table | 1 MiB | 441.1 | 2.267 | true |
| crc32 | slice-by-8 | 1 MiB | 2363.8 | 0.423 | true |
| crc32 | SSE4.2 CRC32C experiment | 1 MiB | 9511.3 | 0.105 | false |
| adler32 | miniz 8x | 1 MiB | 3158.8 | 0.317 | true |
| adler32 | scalar 16x | 1 MiB | 3415.9 | 0.293 | true |
| adler32 | SSSE3 weighted | 1 MiB | 12656.8 | 0.079 | true |
| adler32 | AVX2 weighted | 1 MiB | 15259.2 | 0.066 | true |
| crc32 | miniz table | 64 MiB | 438.4 | 145.985 | true |
| crc32 | slice-by-8 | 64 MiB | 2341.1 | 27.337 | true |
| crc32 | SSE4.2 CRC32C experiment | 64 MiB | 9113.8 | 7.022 | false |
| adler32 | miniz 8x | 64 MiB | 3107.7 | 20.594 | true |
| adler32 | scalar 16x | 64 MiB | 3317.8 | 19.290 | true |
| adler32 | SSSE3 weighted | 64 MiB | 11655.6 | 5.491 | true |
| adler32 | AVX2 weighted | 64 MiB | 13851.0 | 4.621 | true |
| Algorithm | Baseline | Optimized | Speedup |
|---|---|---|---|
| CRC-32 slice-by-8 | 438.4 MB/s | 2341.1 MB/s | 5.34x |
| Adler-32 AVX2 | 3107.7 MB/s | 13851.0 MB/s | 4.46x |
4. Codec Results
| Operation | Dataset | Baseline | AMD experiment | Speedup |
|---|---|---|---|---|
| compress | zeros | 388.9 MB/s | 438.4 MB/s | 1.13x |
| compress | text | 401.3 MB/s | 469.1 MB/s | 1.17x |
| compress | blocks | 124.0 MB/s | 120.4 MB/s | 0.97x |
| compress | random | 33.1 MB/s | 33.2 MB/s | 1.00x |
| decompress | zeros | 455.1 MB/s | 531.3 MB/s | 1.17x |
| decompress | text | 2167.9 MB/s | 4588.8 MB/s | 2.12x |
| decompress | blocks | 1877.1 MB/s | 3248.8 MB/s | 1.73x |
| decompress | random | 2225.6 MB/s | 4650.9 MB/s | 2.09x |
5. Microbenchmarks
| Operation | Variant | Work size | MB/s | Median ms | Correct |
|---|---|---|---|---|---|
| match compare | scalar | 67108864 | 2741.6 | 23.344 | true |
| match compare | AVX2 | 67108864 | 12356.8 | 5.179 | true |
| copy | libc memcpy | 67108864 | 12436.9 | 5.146 | true |
| copy | AVX2 64-byte | 67108864 | 6365.6 | 10.054 | true |
| prefetch chain | no prefetch | 1048576 | 4406.7 | 3.631 | true |
| prefetch chain | builtin prefetch | 1048576 | 4131.1 | 3.873 | true |
scalar compare: 2741.6 MB/s
AVX2 compare: 12356.8 MB/s
static size_t equal_prefix_avx2(
const uint8_t* a,
const uint8_t* b,
size_t limit) {
size_t i = 0;
while (i + 32 <= limit) {
const __m256i va = _mm256_loadu_si256(
reinterpret_cast<const __m256i*>(a + i));
const __m256i vb = _mm256_loadu_si256(
reinterpret_cast<const __m256i*>(b + i));
const __m256i eq = _mm256_cmpeq_epi8(va, vb);
const uint32_t mask =
static_cast<uint32_t>(_mm256_movemask_epi8(eq));
if (mask != 0xffffffffu) {
return i + static_cast<size_t>(__builtin_ctz(~mask));
}
i += 32;
}
while (i < limit && a[i] == b[i]) {
++i;
}
return i;
}
static inline void copy64_avx2(uint8_t* dst, const uint8_t* src) {
const __m256i a = _mm256_loadu_si256(
reinterpret_cast<const __m256i*>(src));
const __m256i b = _mm256_loadu_si256(
reinterpret_cast<const __m256i*>(src + 32));
_mm256_storeu_si256(reinterpret_cast<__m256i*>(dst), a);
_mm256_storeu_si256(reinterpret_cast<__m256i*>(dst + 32), b);
}
for (size_t i = 0; i < chain_length; ++i) {
const Link* next = cursor->next;
if (next != nullptr) {
__builtin_prefetch(next, 0, 1);
}
checksum += cursor->value;
cursor = next;
}
6. Apple Silicon Comparison
| Component | Baseline | Optimized | Speedup |
|---|---|---|---|
| CRC-32 ARM hardware | 445 MB/s | 47,300 MB/s | 106x |
| CRC-32 slice-by-8 | 445 MB/s | 2,135 MB/s | 4.8x |
| Adler-32 NEON | 3,408 MB/s | 24,900 MB/s | 7.3x |
| String compare | about 500 MB/s | about 2,000 MB/s | 4x |
| Compression | 800 MB/s | about 880 MB/s | 1.1x |
| Decompression | 2,000 MB/s | about 2,200 MB/s | 1.1x |
checksums and regular byte scans widen well
full compression gains are limited by serial, branchy codec work
custom copy code can lose to platform libc
7. Correctness Rules
static bool same_crc32_behavior(const uint8_t* p, size_t n) {
const uint32_t one_shot_ref = crc32_reference(0, p, n);
const uint32_t one_shot_new = crc32_candidate(0, p, n);
const size_t split = n / 2;
uint32_t incremental = crc32_candidate(0, p, split);
incremental = crc32_candidate(incremental, p + split, n - split);
return one_shot_new == one_shot_ref &&
incremental == one_shot_ref;
}
measure speed only after proving the replacement is semantically identical
8. Practical Takeaways
optimize where the work is regular enough to widen
verify that the widened work is the same work
then measure whether the full system actually notices
Comments