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

Function sz_fill_haswell

include/stringzilla/memory.h:389–430  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

387#endif
388
389SZ_PUBLIC void sz_fill_haswell(sz_ptr_t target, sz_size_t length, sz_u8_t value) {
390 char value_char = *(char *)&value;
391 __m256i value_vec = _mm256_set1_epi8(value_char);
392 // The naive implementation of this function is very simple.
393 // It assumes the CPU is great at handling unaligned "stores".
394 //
395 // for (; length >= 32; target += 32, length -= 32) _mm256_storeu_si256(target, value_vec);
396 // sz_fill_serial(target, length, value);
397 //
398 // When the buffer is small, there isn't much to innovate.
399 if (length <= 32) sz_fill_serial(target, length, value);
400 // When the buffer is aligned, we can avoid any split-stores.
401 else {
402 sz_size_t head_length = (32 - ((sz_size_t)target % 32)) % 32; // 31 or less.
403 sz_size_t tail_length = (sz_size_t)(target + length) % 32; // 31 or less.
404 sz_size_t body_length = length - head_length - tail_length; // Multiple of 32.
405 sz_u16_t value16 = (sz_u16_t)value * 0x0101u;
406 sz_u32_t value32 = (sz_u32_t)value16 * 0x00010001u;
407 sz_u64_t value64 = (sz_u64_t)value32 * 0x0000000100000001ull;
408
409 // Fill the head of the buffer. This part is much cleaner with AVX-512.
410 if (head_length & 1) *(sz_u8_t *)target = value, target++, head_length--;
411 if (head_length & 2) *(sz_u16_t *)target = value16, target += 2, head_length -= 2;
412 if (head_length & 4) *(sz_u32_t *)target = value32, target += 4, head_length -= 4;
413 if (head_length & 8) *(sz_u64_t *)target = value64, target += 8, head_length -= 8;
414 if (head_length & 16)
415 _mm_store_si128((__m128i *)target, _mm_set1_epi8(value_char)), target += 16, head_length -= 16;
416 sz_assert_((sz_size_t)target % 32 == 0 && "Target is supposed to be aligned to the YMM register size.");
417
418 // Fill the aligned body of the buffer.
419 for (; body_length >= 32; target += 32, body_length -= 32) _mm256_store_si256((__m256i *)target, value_vec);
420
421 // Fill the tail of the buffer. This part is much cleaner with AVX-512.
422 sz_assert_((sz_size_t)target % 32 == 0 && "Target is supposed to be aligned to the YMM register size.");
423 if (tail_length & 16)
424 _mm_store_si128((__m128i *)target, _mm_set1_epi8(value_char)), target += 16, tail_length -= 16;
425 if (tail_length & 8) *(sz_u64_t *)target = value64, target += 8, tail_length -= 8;
426 if (tail_length & 4) *(sz_u32_t *)target = value32, target += 4, tail_length -= 4;
427 if (tail_length & 2) *(sz_u16_t *)target = value16, target += 2, tail_length -= 2;
428 if (tail_length & 1) *(sz_u8_t *)target = value, target++, tail_length--;
429 }
430}
431
432SZ_PUBLIC void sz_copy_haswell(sz_ptr_t target, sz_cptr_t source, sz_size_t length) {
433 // The naive implementation of this function is very simple.

Callers 1

sz_fillFunction · 0.85

Calls 1

sz_fill_serialFunction · 0.85

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…