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

Function sz_move_haswell

include/stringzilla/memory.h:544–602  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

542}
543
544SZ_PUBLIC void sz_move_haswell(sz_ptr_t target, sz_cptr_t source, sz_size_t length) {
545
546 if (length < 8) {
547 if (target < source)
548 while (length--) *(target++) = *(source++);
549 else {
550 // Jump to the end and walk backwards:
551 target += length, source += length;
552 while (length--) *(--target) = *(--source);
553 }
554 }
555 // The next few sections are identical here and in the `sz_copy_haswell` function.
556 // We can use 2x 64-bit interleaving loads for each string, and then compare them for equality.
557 // The same approach is used in GLibC and was suggest by Denis Yaroshevskiy.
558 // https://codebrowser.dev/glibc/glibc/sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S.html#518
559 // It shouldn't improve performance on microbenchmarks, but should be better in practice.
560 else if (length <= 16) {
561 sz_u64_t source_first_word = *(sz_u64_t const *)(source);
562 sz_u64_t source_second_word = *(sz_u64_t const *)(source + length - 8);
563 sz_u64_t *target_first_word_ptr = (sz_u64_t *)(target);
564 sz_u64_t *target_second_word_ptr = (sz_u64_t *)(target + length - 8);
565 *target_first_word_ptr = source_first_word;
566 *target_second_word_ptr = source_second_word;
567 }
568 // We can use 2x 128-bit interleaving loads for each string, and then compare them for equality.
569 else if (length <= 32) {
570 sz_u128_vec_t source_first_vec, source_second_vec;
571 sz_u128_vec_t *target_first_word_ptr, *target_second_word_ptr;
572 source_first_vec.xmm = _mm_lddqu_si128((__m128i const *)(source));
573 source_second_vec.xmm = _mm_lddqu_si128((__m128i const *)(source + length - 16));
574 target_first_word_ptr = (sz_u128_vec_t *)(target);
575 target_second_word_ptr = (sz_u128_vec_t *)(target + length - 16);
576 _mm_storeu_si128(&target_first_word_ptr->xmm, source_first_vec.xmm);
577 _mm_storeu_si128(&target_second_word_ptr->xmm, source_second_vec.xmm);
578 }
579 // We can use 2x 256-bit interleaving loads for each string, and then compare them for equality.
580 else if (length <= 64) {
581 sz_u256_vec_t source_first_vec, source_second_vec;
582 sz_u256_vec_t *target_first_word_ptr, *target_second_word_ptr;
583 source_first_vec.ymm = _mm256_lddqu_si256((__m256i const *)(source));
584 source_second_vec.ymm = _mm256_lddqu_si256((__m256i const *)(source + length - 32));
585 target_first_word_ptr = (sz_u256_vec_t *)(target);
586 target_second_word_ptr = (sz_u256_vec_t *)(target + length - 32);
587 _mm256_storeu_si256(&target_first_word_ptr->ymm, source_first_vec.ymm);
588 _mm256_storeu_si256(&target_second_word_ptr->ymm, source_second_vec.ymm);
589 }
590 // When dealing with larger arrays, we keep things simple:
591 else if (target < source || target >= source + length) {
592 for (; length >= 32; target += 32, source += 32, length -= 32)
593 _mm256_storeu_si256((__m256i *)target, _mm256_lddqu_si256((__m256i const *)source));
594 while (length--) *(target++) = *(source++);
595 }
596 else {
597 // Jump to the end and walk backwards:
598 for (target += length, source += length; length >= 32; length -= 32)
599 _mm256_storeu_si256((__m256i *)(target -= 32), _mm256_lddqu_si256((__m256i const *)(source -= 32)));
600 while (length--) *(--target) = *(--source);
601 }

Callers 1

sz_moveFunction · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…