| 999 | #endif |
| 1000 | |
| 1001 | SZ_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 | } |
no test coverage detected
searching dependent graphs…