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

Function sz_lookup_haswell

include/stringzilla/memory.h:604–739  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

602}
603
604SZ_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);

Callers 1

sz_lookupFunction · 0.85

Calls 1

sz_lookup_serialFunction · 0.85

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…