| 602 | } |
| 603 | |
| 604 | SZ_PUBLIC void sz_lookup_haswell(sz_ptr_t target, sz_size_t length, sz_cptr_t source, |
| 605 | char const lut[sz_at_least_(256)]) { |
| 606 | |
| 607 | // If the input is tiny (especially smaller than the look-up table itself), we may end up paying |
| 608 | // more for organizing the SIMD registers and changing the CPU state, than for the actual computation. |
| 609 | // But if at least 3 cache lines are touched, the AVX-2 implementation should be faster. |
| 610 | if (length <= 128) { |
| 611 | sz_lookup_serial(target, length, source, lut); |
| 612 | return; |
| 613 | } |
| 614 | |
| 615 | // We need to pull the lookup table into 8x YMM registers. |
| 616 | // The biggest issue is reorganizing the data in the lookup table, as AVX2 doesn't have 256-bit shuffle, |
| 617 | // it only has 128-bit "within-lane" shuffle. Still, it's wiser to use full YMM registers, instead of XMM, |
| 618 | // so that we can at least compensate high latency with twice larger window and one more level of lookup. |
| 619 | sz_u256_vec_t lut_0_to_15_vec, lut_16_to_31_vec, lut_32_to_47_vec, lut_48_to_63_vec, // |
| 620 | lut_64_to_79_vec, lut_80_to_95_vec, lut_96_to_111_vec, lut_112_to_127_vec, // |
| 621 | lut_128_to_143_vec, lut_144_to_159_vec, lut_160_to_175_vec, lut_176_to_191_vec, // |
| 622 | lut_192_to_207_vec, lut_208_to_223_vec, lut_224_to_239_vec, lut_240_to_255_vec; |
| 623 | |
| 624 | lut_0_to_15_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut))); |
| 625 | lut_16_to_31_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 16))); |
| 626 | lut_32_to_47_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 32))); |
| 627 | lut_48_to_63_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 48))); |
| 628 | lut_64_to_79_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 64))); |
| 629 | lut_80_to_95_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 80))); |
| 630 | lut_96_to_111_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 96))); |
| 631 | lut_112_to_127_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 112))); |
| 632 | lut_128_to_143_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 128))); |
| 633 | lut_144_to_159_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 144))); |
| 634 | lut_160_to_175_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 160))); |
| 635 | lut_176_to_191_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 176))); |
| 636 | lut_192_to_207_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 192))); |
| 637 | lut_208_to_223_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 208))); |
| 638 | lut_224_to_239_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 224))); |
| 639 | lut_240_to_255_vec.ymm = _mm256_broadcastsi128_si256(_mm_lddqu_si128((__m128i const *)(lut + 240))); |
| 640 | |
| 641 | // Assuming each lookup is performed within 16 elements of 256, we need to reduce the scope by 16x = 2^4. |
| 642 | sz_u256_vec_t not_first_bit_vec, not_second_bit_vec, not_third_bit_vec, not_fourth_bit_vec; |
| 643 | |
| 644 | /// Top and bottom nibbles of the source are used separately. |
| 645 | sz_u256_vec_t source_vec, source_bot_vec; |
| 646 | sz_u256_vec_t blended_0_to_31_vec, blended_32_to_63_vec, blended_64_to_95_vec, blended_96_to_127_vec, |
| 647 | blended_128_to_159_vec, blended_160_to_191_vec, blended_192_to_223_vec, blended_224_to_255_vec; |
| 648 | |
| 649 | // Handling the head. |
| 650 | while (length >= 32) { |
| 651 | // Load and separate the nibbles of each byte in the source. |
| 652 | source_vec.ymm = _mm256_lddqu_si256((__m256i const *)source); |
| 653 | source_bot_vec.ymm = _mm256_and_si256(source_vec.ymm, _mm256_set1_epi8((char)0x0F)); |
| 654 | |
| 655 | // In the first round, we select using the 4th bit. |
| 656 | not_fourth_bit_vec.ymm = _mm256_cmpeq_epi8( // |
| 657 | _mm256_and_si256(_mm256_set1_epi8((char)0x10), source_vec.ymm), _mm256_setzero_si256()); |
| 658 | blended_0_to_31_vec.ymm = _mm256_blendv_epi8( // |
| 659 | _mm256_shuffle_epi8(lut_16_to_31_vec.ymm, source_bot_vec.ymm), // |
| 660 | _mm256_shuffle_epi8(lut_0_to_15_vec.ymm, source_bot_vec.ymm), // |
| 661 | not_fourth_bit_vec.ymm); |
no test coverage detected
searching dependent graphs…