57
57
#include <stdlib.h>
58
58
#include <string.h>
59
59
#include <unistd.h>
60
+ #if HAVE_X86_SIMD
61
+ #include <immintrin.h>
62
+ #endif
60
63
61
64
/* The default hashing function uses the SipHash implementation in siphash.c. */
62
65
@@ -669,6 +672,51 @@ static int expand(hashtable *ht, size_t size, int *malloc_failed) {
669
672
return resize (ht , size , malloc_failed );
670
673
}
671
674
675
+ /* Checks if a candidate entry in a bucket matches the given key.
676
+ *
677
+ * This function examines a specific position in a bucket to determine if the
678
+ * entry at that position matches the provided key. If a match is found, it
679
+ * updates the position and table index pointers and returns 1. Otherwise,
680
+ * it returns 0. */
681
+ static inline int checkCandidateInBucket (hashtable * ht , bucket * b , int pos , const void * key , int table , int * pos_in_bucket , int * table_index ) {
682
+ /* It's a candidate. */
683
+ void * entry = b -> entries [pos ];
684
+ const void * elem_key = entryGetKey (ht , entry );
685
+ if (compareKeys (ht , key , elem_key ) == 0 ) {
686
+ /* It's a match. */
687
+ assert (pos_in_bucket != NULL );
688
+ * pos_in_bucket = pos ;
689
+ if (table_index ) * table_index = table ;
690
+ return 1 ;
691
+ }
692
+ return 0 ;
693
+ }
694
+
695
+ #if HAVE_X86_SIMD
696
+ ATTRIBUTE_TARGET_SSE2
697
+ static int findKeyInBucketSSE2 (hashtable * ht , bucket * b , uint8_t h2 , const void * key , int table , int * pos_in_bucket , int * table_index ) {
698
+ /* Get the bucket's presence mask - indicates which positions are filled. */
699
+ BUCKET_BITS_TYPE presence_mask = b -> presence & ((1 << ENTRIES_PER_BUCKET ) - 1 );
700
+ __m128i hash_vector = _mm_loadu_si128 ((__m128i * )b -> hashes );
701
+ __m128i h2_vector = _mm_set1_epi8 (h2 );
702
+ /* Compare all hash values against the target hash simultaneously.
703
+ * The result is a vector of 16 bytes, where each byte is 0xFF if
704
+ * the corresponding hash matches the target hash, and 0x00 if it
705
+ * doesn't. */
706
+ __m128i result = _mm_cmpeq_epi8 (hash_vector , h2_vector );
707
+ BUCKET_BITS_TYPE newmask = _mm_movemask_epi8 (result );
708
+ /* Only consider positions that are both filled (presence) and match the hash (newmask). */
709
+ newmask &= presence_mask ;
710
+ while (newmask > 0 ) {
711
+ int pos = __builtin_ctz (newmask );
712
+ if (checkCandidateInBucket (ht , b , pos , key , table , pos_in_bucket , table_index )) return 1 ;
713
+ /* Clear the processed bit and continue with next match. */
714
+ newmask &= ~(1 << pos );
715
+ }
716
+ return 0 ;
717
+ }
718
+ #endif
719
+
672
720
/* Finds an entry matching the key. If a match is found, returns a pointer to
673
721
* the bucket containing the matching entry and points 'pos_in_bucket' to the
674
722
* index within the bucket. Returns NULL if no matching entry was found.
@@ -693,21 +741,16 @@ static bucket *findBucket(hashtable *ht, uint64_t hash, const void *key, int *po
693
741
}
694
742
bucket * b = & ht -> tables [table ][bucket_idx ];
695
743
do {
744
+ #if HAVE_X86_SIMD
745
+ /* All x86-64 CPUs have SSE2. */
746
+ if (findKeyInBucketSSE2 (ht , b , h2 , key , table , pos_in_bucket , table_index )) return b ;
747
+ #else
696
748
/* Find candidate entries with presence flag set and matching h2 hash. */
697
749
for (int pos = 0 ; pos < numBucketPositions (b ); pos ++ ) {
698
- if (isPositionFilled (b , pos ) && b -> hashes [pos ] == h2 ) {
699
- /* It's a candidate. */
700
- void * entry = b -> entries [pos ];
701
- const void * elem_key = entryGetKey (ht , entry );
702
- if (compareKeys (ht , key , elem_key ) == 0 ) {
703
- /* It's a match. */
704
- assert (pos_in_bucket != NULL );
705
- * pos_in_bucket = pos ;
706
- if (table_index ) * table_index = table ;
707
- return b ;
708
- }
709
- }
750
+ if (isPositionFilled (b , pos ) && b -> hashes [pos ] == h2 &&
751
+ checkCandidateInBucket (ht , b , pos , key , table , pos_in_bucket , table_index )) return b ;
710
752
}
753
+ #endif
711
754
b = getChildBucket (b );
712
755
} while (b != NULL );
713
756
}
0 commit comments