MCPcopy Create free account
hub / github.com/ashvardanian/StringZilla / sz_lookup_ice

Function sz_lookup_ice

include/stringzilla/memory.h:1001–1070  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

999#endif
1000
1001SZ_PUBLIC void sz_lookup_ice(sz_ptr_t target, sz_size_t length, sz_cptr_t source, char const lut[sz_at_least_(256)]) {
1002
1003 // If the input is tiny (especially smaller than the look-up table itself), we may end up paying
1004 // more for organizing the SIMD registers and changing the CPU state, than for the actual computation.
1005 // But if at least 3 cache lines are touched, the AVX-512 implementation should be faster.
1006 if (length <= 128) {
1007 sz_lookup_serial(target, length, source, lut);
1008 return;
1009 }
1010
1011 // When the buffer is over 64 bytes, it's guaranteed to touch at least two cache lines - the head and tail,
1012 // and may include more cache-lines in-between. Knowing this, we can avoid expensive unaligned stores
1013 // by computing 2 masks - for the head and tail, using masked stores for the head and tail, and unmasked
1014 // for the body.
1015 sz_size_t head_length = (64 - ((sz_size_t)target % 64)) % 64; // 63 or less.
1016 sz_size_t tail_length = (sz_size_t)(target + length) % 64; // 63 or less.
1017 __mmask64 head_mask = sz_u64_mask_until_(head_length);
1018 __mmask64 tail_mask = sz_u64_mask_until_(tail_length);
1019
1020 // We use VPERMI2B (`_mm512_permutex2var_epi8`) to perform 256-entry lookups efficiently.
1021 // VPERMI2B uses bit 6 of each index to select between two 64-byte tables, allowing us to
1022 // cover 128 entries per instruction (2 instructions for all 256 entries).
1023 //
1024 // For the high-bit (bit 7) selection, we use VPMOVB2M (`_mm512_movepi8_mask`) which extracts
1025 // the sign bit of each byte directly to a mask register. This goes to port 0 on Intel,
1026 // avoiding the port 5 bottleneck that VPTESTMB would cause.
1027 sz_u512_vec_t lut_0_to_63_vec, lut_64_to_127_vec, lut_128_to_191_vec, lut_192_to_255_vec;
1028 lut_0_to_63_vec.zmm = _mm512_loadu_si512((lut));
1029 lut_64_to_127_vec.zmm = _mm512_loadu_si512((lut + 64));
1030 lut_128_to_191_vec.zmm = _mm512_loadu_si512((lut + 128));
1031 lut_192_to_255_vec.zmm = _mm512_loadu_si512((lut + 192));
1032
1033 __mmask64 high_bit_mask;
1034 sz_u512_vec_t source_vec, low_half_vec, high_half_vec, result_vec;
1035
1036 // Handling the head.
1037 if (head_length) {
1038 source_vec.zmm = _mm512_maskz_loadu_epi8(head_mask, source);
1039 // VPERMI2B: bit 6 selects between the two tables, bits 0-5 index within each
1040 low_half_vec.zmm = _mm512_permutex2var_epi8(lut_0_to_63_vec.zmm, source_vec.zmm, lut_64_to_127_vec.zmm);
1041 high_half_vec.zmm = _mm512_permutex2var_epi8(lut_128_to_191_vec.zmm, source_vec.zmm, lut_192_to_255_vec.zmm);
1042 // VPMOVB2M: extract bit 7 (sign bit) of each byte directly to mask - uses port 0, not port 5
1043 high_bit_mask = _mm512_movepi8_mask(source_vec.zmm);
1044 result_vec.zmm = _mm512_mask_blend_epi8(high_bit_mask, low_half_vec.zmm, high_half_vec.zmm);
1045 _mm512_mask_storeu_epi8(target, head_mask, result_vec.zmm);
1046 source += head_length, target += head_length, length -= head_length;
1047 }
1048
1049 // Handling the body in 64-byte chunks aligned to cache-line boundaries with respect to `target`.
1050 while (length >= 64) {
1051 source_vec.zmm = _mm512_loadu_si512(source);
1052 low_half_vec.zmm = _mm512_permutex2var_epi8(lut_0_to_63_vec.zmm, source_vec.zmm, lut_64_to_127_vec.zmm);
1053 high_half_vec.zmm = _mm512_permutex2var_epi8(lut_128_to_191_vec.zmm, source_vec.zmm, lut_192_to_255_vec.zmm);
1054 high_bit_mask = _mm512_movepi8_mask(source_vec.zmm);
1055 result_vec.zmm = _mm512_mask_blend_epi8(high_bit_mask, low_half_vec.zmm, high_half_vec.zmm);
1056 _mm512_store_si512(target, result_vec.zmm); //! Aligned store, our main weapon!
1057 source += 64, target += 64, length -= 64;
1058 }

Callers 1

sz_lookupFunction · 0.85

Calls 2

sz_lookup_serialFunction · 0.85
sz_u64_mask_until_Function · 0.85

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…