@@ -68,6 +68,11 @@ chaining would be substantial (100% with typical malloc overhead).
6868The initial probe index is computed as hash mod the table size. Subsequent
6969probe indices are computed as explained in Objects/dictobject.c.
7070
71+ To improve cache locality, each probe is done in pairs.
72+ After the probe is examined, an adjacent entry is then examined as well.
73+ The likelihood is that an adjacent entry is in the same cache line and
74+ can be examined more cheaply than another probe elsewhere in memory.
75+
7176All arithmetic on hash should ignore overflow.
7277
7378Unlike the dictionary implementation, the lookkey functions can return
@@ -77,7 +82,7 @@ NULL if the rich comparison returns an error.
7782static setentry *
7883set_lookkey (PySetObject * so , PyObject * key , Py_hash_t hash )
7984{
80- size_t i ; /* Unsigned for defined overflow behavior. */
85+ size_t i , j ; /* Unsigned for defined overflow behavior. */
8186 size_t perturb ;
8287 setentry * freeslot ;
8388 size_t mask = so -> mask ;
@@ -90,7 +95,6 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
9095 entry = & table [i ];
9196 if (entry -> key == NULL || entry -> key == key )
9297 return entry ;
93-
9498 if (entry -> hash == hash ) {
9599 startkey = entry -> key ;
96100 Py_INCREF (startkey );
@@ -107,14 +111,45 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
107111 return set_lookkey (so , key , hash );
108112 }
109113 }
110-
111114 freeslot = (entry -> key == dummy ) ? entry : NULL ;
112115
113116 /* In the loop, key == dummy is by far (factor of 100s)
114117 the least likely outcome, so test for that last. */
115- for (perturb = hash ; ; perturb >>= PERTURB_SHIFT ) {
118+ j = i ;
119+ perturb = hash ;
120+ while (1 ) {
121+ j ^= 1 ;
122+ entry = & table [j ];
123+ if (entry -> key == NULL ) {
124+ if (freeslot != NULL )
125+ entry = freeslot ;
126+ break ;
127+ }
128+ if (entry -> key == key )
129+ break ;
130+ if (entry -> hash == hash ) {
131+ startkey = entry -> key ;
132+ Py_INCREF (startkey );
133+ cmp = PyObject_RichCompareBool (startkey , key , Py_EQ );
134+ Py_DECREF (startkey );
135+ if (cmp < 0 )
136+ return NULL ;
137+ if (table == so -> table && entry -> key == startkey ) {
138+ if (cmp > 0 )
139+ break ;
140+ }
141+ else {
142+ return set_lookkey (so , key , hash );
143+ }
144+ }
145+ if (entry -> key == dummy && freeslot == NULL )
146+ freeslot = entry ;
147+
116148 i = i * 5 + perturb + 1 ;
117- entry = & table [i & mask ];
149+ j = i & mask ;
150+ perturb >>= PERTURB_SHIFT ;
151+
152+ entry = & table [j ];
118153 if (entry -> key == NULL ) {
119154 if (freeslot != NULL )
120155 entry = freeslot ;
@@ -134,14 +169,12 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
134169 break ;
135170 }
136171 else {
137- /* The compare did major nasty stuff to the
138- * set: start over.
139- */
140172 return set_lookkey (so , key , hash );
141173 }
142174 }
143175 if (entry -> key == dummy && freeslot == NULL )
144176 freeslot = entry ;
177+
145178 }
146179 return entry ;
147180}
@@ -154,7 +187,7 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
154187static setentry *
155188set_lookkey_unicode (PySetObject * so , PyObject * key , Py_hash_t hash )
156189{
157- size_t i ; /* Unsigned for defined overflow behavior. */
190+ size_t i , j ; /* Unsigned for defined overflow behavior. */
158191 size_t perturb ;
159192 setentry * freeslot ;
160193 size_t mask = so -> mask ;
@@ -169,6 +202,7 @@ set_lookkey_unicode(PySetObject *so, PyObject *key, Py_hash_t hash)
169202 so -> lookup = set_lookkey ;
170203 return set_lookkey (so , key , hash );
171204 }
205+
172206 i = (size_t )hash & mask ;
173207 entry = & table [i ];
174208 if (entry -> key == NULL || entry -> key == key )
@@ -181,11 +215,37 @@ set_lookkey_unicode(PySetObject *so, PyObject *key, Py_hash_t hash)
181215 freeslot = NULL ;
182216 }
183217
184- /* In the loop, key == dummy is by far (factor of 100s) the
185- least likely outcome, so test for that last. */
186- for (perturb = hash ; ; perturb >>= PERTURB_SHIFT ) {
218+ entry = & table [i ^ 1 ];
219+ if (entry -> key == NULL )
220+ return freeslot == NULL ? entry : freeslot ;
221+ if (entry -> key == key
222+ || (entry -> hash == hash
223+ && entry -> key != dummy
224+ && unicode_eq (entry -> key , key )))
225+ return entry ;
226+ if (entry -> key == dummy && freeslot == NULL )
227+ freeslot = entry ;
228+
229+ j = i ;
230+ perturb = hash ;
231+ while (1 ) {
232+ j ^= 1 ;
233+ entry = & table [j ];
234+ if (entry -> key == NULL )
235+ return freeslot == NULL ? entry : freeslot ;
236+ if (entry -> key == key
237+ || (entry -> hash == hash
238+ && entry -> key != dummy
239+ && unicode_eq (entry -> key , key )))
240+ return entry ;
241+ if (entry -> key == dummy && freeslot == NULL )
242+ freeslot = entry ;
243+
187244 i = i * 5 + perturb + 1 ;
188- entry = & table [i & mask ];
245+ j = i & mask ;
246+ perturb >>= PERTURB_SHIFT ;
247+
248+ entry = & table [j ];
189249 if (entry -> key == NULL )
190250 return freeslot == NULL ? entry : freeslot ;
191251 if (entry -> key == key
@@ -244,17 +304,23 @@ is responsible for incref'ing `key`.
244304static void
245305set_insert_clean (PySetObject * so , PyObject * key , Py_hash_t hash )
246306{
247- size_t i ;
248- size_t perturb ;
249- size_t mask = (size_t )so -> mask ;
250307 setentry * table = so -> table ;
251308 setentry * entry ;
309+ size_t perturb = hash ;
310+ size_t mask = (size_t )so -> mask ;
311+ size_t i , j ;
252312
253- i = (size_t )hash & mask ;
254- entry = & table [i ];
255- for (perturb = hash ; entry -> key != NULL ; perturb >>= PERTURB_SHIFT ) {
313+ i = j = (size_t )hash & mask ;
314+ while (1 ) {
315+ entry = & table [j ];
316+ if (entry -> key == NULL )
317+ break ;
318+ entry = & table [j ^ 1 ];
319+ if (entry -> key == NULL )
320+ break ;
256321 i = i * 5 + perturb + 1 ;
257- entry = & table [i & mask ];
322+ j = i & mask ;
323+ perturb >>= PERTURB_SHIFT ;
258324 }
259325 so -> fill ++ ;
260326 entry -> key = key ;
0 commit comments