Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable WIMF changes are recorded here. The project follows semantic version

## 2.2.0 - Unreleased

- Upgraded the scalar CRC-32 to slice-by-8: all eight slice tables are derived
from the polynomial at compile time, and the bulk loop consumes eight bytes
per iteration instead of one (typically 4-6x table throughput on every
platform, including x86 where no IEEE hardware CRC instruction exists).
- First slice of the context-modeled entropy stage (known-flaws A3): lossy
wavelet tiles now use marker-free (run, zigzag) coefficient tokens flagged by
the tile's reversible byte value 2, removing one mandatory byte per token
Expand Down
35 changes: 35 additions & 0 deletions src/v2_simd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "v2_simd.hpp"

#include <cstring>

#if defined(WIMF_AVX2_KERNELS) && defined(_MSC_VER) && !defined(__clang__)
#include <immintrin.h>
#include <intrin.h>
Expand All @@ -31,6 +33,24 @@ struct CrcTable {
};
constexpr CrcTable kCrcTable{};

// Slice-by-8 tables: entry [k][i] is the CRC after consuming byte i followed
// by k zero bytes, derived entirely from the polynomial at compile time.
// Processing eight bytes per iteration removes most of the serial dependency
// chain of the classic one-byte table loop (typically 4-6x throughput).
struct CrcSlices {
uint32_t entries[8][256];
constexpr CrcSlices() : entries{} {
for (uint32_t i = 0; i < 256; ++i) {
entries[0][i] = crc_entry(i);
for (int k = 1; k < 8; ++k) {
const uint32_t previous = entries[k - 1][i];
entries[k][i] = (previous >> 8) ^ crc_entry(previous & 0xFFu);
}
}
}
};
constexpr CrcSlices kCrcSlices{};

struct Features {
bool avx2 = false;
bool hardware_crc32 = false;
Expand Down Expand Up @@ -83,6 +103,21 @@ bool has_hardware_crc32() noexcept { return features().hardware_crc32; }

uint32_t crc32_table(const uint8_t* data, size_t size) noexcept {
uint32_t crc = 0xFFFFFFFFu;
// Slice-by-8 main loop: fold eight bytes per iteration through the
// precomputed slice tables. Byte order matches the little-endian load of
// the first four bytes into the running CRC.
while (size >= 8) {
uint32_t low, high;
std::memcpy(&low, data, sizeof(low));
std::memcpy(&high, data + 4, sizeof(high));
low ^= crc;
crc = kCrcSlices.entries[7][low & 0xFFu] ^ kCrcSlices.entries[6][(low >> 8) & 0xFFu] ^
kCrcSlices.entries[5][(low >> 16) & 0xFFu] ^ kCrcSlices.entries[4][low >> 24] ^
kCrcSlices.entries[3][high & 0xFFu] ^ kCrcSlices.entries[2][(high >> 8) & 0xFFu] ^
kCrcSlices.entries[1][(high >> 16) & 0xFFu] ^ kCrcSlices.entries[0][high >> 24];
data += 8;
size -= 8;
}
for (size_t i = 0; i < size; ++i)
crc = (crc >> 8) ^ kCrcTable.entries[(crc ^ data[i]) & 0xFFu];
return ~crc;
Expand Down
Loading