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

Function sz_equal_haswell

include/stringzilla/compare.h:255–310  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

253}
254
255SZ_PUBLIC sz_bool_t sz_equal_haswell(sz_cptr_t a, sz_cptr_t b, sz_size_t length) {
256
257 if (length < 8) {
258 sz_cptr_t const a_end = a + length;
259 while (a != a_end && *a == *b) a++, b++;
260 return (sz_bool_t)(a_end == a);
261 }
262 // We can use 2x 64-bit interleaving loads for each string, and then compare them for equality.
263 // The same approach is used in GLibC and was suggest by Denis Yaroshevskiy.
264 // https://codebrowser.dev/glibc/glibc/sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S.html#518
265 // It shouldn't improve performance on microbenchmarks, but should be better in practice.
266 else if (length <= 16) {
267 sz_u64_t a_first_word = sz_u64_load(a).u64;
268 sz_u64_t b_first_word = sz_u64_load(b).u64;
269 sz_u64_t a_second_word = sz_u64_load(a + length - 8).u64;
270 sz_u64_t b_second_word = sz_u64_load(b + length - 8).u64;
271 return (sz_bool_t)((a_first_word == b_first_word) & (a_second_word == b_second_word));
272 }
273 // We can use 2x 128-bit interleaving loads for each string, and then compare them for equality.
274 else if (length <= 32) {
275 sz_u128_vec_t a_first_vec, b_first_vec, a_second_vec, b_second_vec;
276 a_first_vec.xmm = _mm_lddqu_si128((__m128i const *)(a));
277 b_first_vec.xmm = _mm_lddqu_si128((__m128i const *)(b));
278 a_second_vec.xmm = _mm_lddqu_si128((__m128i const *)(a + length - 16));
279 b_second_vec.xmm = _mm_lddqu_si128((__m128i const *)(b + length - 16));
280 return (sz_bool_t)(_mm_movemask_epi8(_mm_and_si128( //
281 _mm_cmpeq_epi8(a_first_vec.xmm, b_first_vec.xmm),
282 _mm_cmpeq_epi8(a_second_vec.xmm, b_second_vec.xmm))) == 0xFFFF);
283 }
284 // We can use 2x 256-bit interleaving loads for each string, and then compare them for equality.
285 else if (length <= 64) {
286 sz_u256_vec_t a_first_vec, b_first_vec, a_second_vec, b_second_vec;
287 a_first_vec.ymm = _mm256_lddqu_si256((__m256i const *)(a));
288 b_first_vec.ymm = _mm256_lddqu_si256((__m256i const *)(b));
289 a_second_vec.ymm = _mm256_lddqu_si256((__m256i const *)(a + length - 32));
290 b_second_vec.ymm = _mm256_lddqu_si256((__m256i const *)(b + length - 32));
291 return (sz_bool_t)(_mm256_movemask_epi8(_mm256_and_si256( //
292 _mm256_cmpeq_epi8(a_first_vec.ymm, b_first_vec.ymm),
293 _mm256_cmpeq_epi8(a_second_vec.ymm, b_second_vec.ymm))) == (int)0xFFFFFFFF);
294 }
295 else {
296 sz_size_t i = 0;
297 sz_u256_vec_t a_vec, b_vec;
298 do {
299 a_vec.ymm = _mm256_lddqu_si256((__m256i const *)(a + i));
300 b_vec.ymm = _mm256_lddqu_si256((__m256i const *)(b + i));
301 // One approach can be to use "movemasks", but we could also use a bitwise
302 // matching like `_mm256_testnzc_si256`.
303 if (_mm256_movemask_epi8(_mm256_cmpeq_epi8(a_vec.ymm, b_vec.ymm)) != (int)0xFFFFFFFF) return sz_false_k;
304 i += 32;
305 } while (i + 32 <= length);
306 a_vec.ymm = _mm256_lddqu_si256((__m256i const *)(a + length - 32));
307 b_vec.ymm = _mm256_lddqu_si256((__m256i const *)(b + length - 32));
308 return (sz_bool_t)(_mm256_movemask_epi8(_mm256_cmpeq_epi8(a_vec.ymm, b_vec.ymm)) == (int)0xFFFFFFFF);
309 }
310}
311
312#if defined(__clang__)

Callers 3

sz_equalFunction · 0.85
sz_find_haswellFunction · 0.85
sz_rfind_haswellFunction · 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…