| 542 | } |
| 543 | |
| 544 | SZ_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 | } |
no outgoing calls
no test coverage detected
searching dependent graphs…