Issue 18771: Make it possible to set the number linear probes at compile-time.

This commit is contained in:
Raymond Hettinger 2013-09-15 14:57:15 -07:00
parent e6d35dba3c
commit 8408dc581e
2 changed files with 28 additions and 7 deletions

View File

@ -444,8 +444,15 @@ Major performance enhancements have been added:
* The UTF-32 decoder is now 3x to 4x faster. * The UTF-32 decoder is now 3x to 4x faster.
* The cost of hash collisions for sets is now reduced. Each hash table * The cost of hash collisions for sets is now reduced. Each hash table
probe now checks a second key/hash pair for each cache line retrieved. probe now checks a series of consecutive, adjacent key/hash pairs before
This exploits cache locality to make collision resolution less expensive. continuing to make random probes through the hash table. This exploits
cache locality to make collision resolution less expensive.
The collision resolution scheme can be described as a hybrid of linear
probing and open addressing. The number of additional linear probes
defaults to nine. This can be changed at compile-time by defining
LINEAR_PROBES to be any value. Set LINEAR_PROBES=0 to turn-off
linear probing entirely.
(Contributed by Raymond Hettinger in :issue"`18771`.) (Contributed by Raymond Hettinger in :issue"`18771`.)

View File

@ -44,7 +44,9 @@ PyObject *_PySet_Dummy = dummy;
/* ======= Begin logic for probing the hash table ========================= */ /* ======= Begin logic for probing the hash table ========================= */
/* This should be >= PySet_MINSIZE - 1 */ /* This should be >= PySet_MINSIZE - 1 */
#ifndef LINEAR_PROBES
#define LINEAR_PROBES 9 #define LINEAR_PROBES 9
#endif
/* This must be >= 1 */ /* This must be >= 1 */
#define PERTURB_SHIFT 5 #define PERTURB_SHIFT 5
@ -55,12 +57,14 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
setentry *table = so->table; setentry *table = so->table;
setentry *freeslot = NULL; setentry *freeslot = NULL;
setentry *entry; setentry *entry;
setentry *limit;
size_t perturb = hash; size_t perturb = hash;
size_t mask = so->mask; size_t mask = so->mask;
size_t i = (size_t)hash; /* Unsigned for defined overflow behavior. */ size_t i = (size_t)hash; /* Unsigned for defined overflow behavior. */
size_t j;
int cmp; int cmp;
#if LINEAR_PROBES
setentry *limit;
size_t j;
#endif
entry = &table[i & mask]; entry = &table[i & mask];
if (entry->key == NULL) if (entry->key == NULL)
@ -84,6 +88,7 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
if (entry->key == dummy && freeslot == NULL) if (entry->key == dummy && freeslot == NULL)
freeslot = entry; freeslot = entry;
#if LINEAR_PROBES
limit = &table[mask]; limit = &table[mask];
for (j = 0 ; j < LINEAR_PROBES ; j++) { for (j = 0 ; j < LINEAR_PROBES ; j++) {
entry = (entry == limit) ? &table[0] : entry + 1; entry = (entry == limit) ? &table[0] : entry + 1;
@ -106,13 +111,14 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
if (entry->key == dummy && freeslot == NULL) if (entry->key == dummy && freeslot == NULL)
freeslot = entry; freeslot = entry;
} }
#endif
perturb >>= PERTURB_SHIFT; perturb >>= PERTURB_SHIFT;
i = i * 5 + 1 + perturb; i = i * 5 + 1 + perturb;
entry = &table[i & mask]; entry = &table[i & mask];
if (entry->key == NULL) if (entry->key == NULL)
break; goto found_null;
} }
found_null: found_null:
return freeslot == NULL ? entry : freeslot; return freeslot == NULL ? entry : freeslot;
@ -129,11 +135,13 @@ set_lookkey_unicode(PySetObject *so, PyObject *key, Py_hash_t hash)
setentry *table = so->table; setentry *table = so->table;
setentry *freeslot = NULL; setentry *freeslot = NULL;
setentry *entry; setentry *entry;
setentry *limit;
size_t perturb = hash; size_t perturb = hash;
size_t mask = so->mask; size_t mask = so->mask;
size_t i = (size_t)hash; size_t i = (size_t)hash;
#if LINEAR_PROBES
setentry *limit;
size_t j; size_t j;
#endif
/* Make sure this function doesn't have to handle non-unicode keys, /* Make sure this function doesn't have to handle non-unicode keys,
including subclasses of str; e.g., one reason to subclass including subclasses of str; e.g., one reason to subclass
@ -157,6 +165,7 @@ set_lookkey_unicode(PySetObject *so, PyObject *key, Py_hash_t hash)
if (entry->key == dummy && freeslot == NULL) if (entry->key == dummy && freeslot == NULL)
freeslot = entry; freeslot = entry;
#if LINEAR_PROBES
limit = &table[mask]; limit = &table[mask];
for (j = 0 ; j < LINEAR_PROBES ; j++) { for (j = 0 ; j < LINEAR_PROBES ; j++) {
entry = (entry == limit) ? &table[0] : entry + 1; entry = (entry == limit) ? &table[0] : entry + 1;
@ -170,13 +179,14 @@ set_lookkey_unicode(PySetObject *so, PyObject *key, Py_hash_t hash)
if (entry->key == dummy && freeslot == NULL) if (entry->key == dummy && freeslot == NULL)
freeslot = entry; freeslot = entry;
} }
#endif
perturb >>= PERTURB_SHIFT; perturb >>= PERTURB_SHIFT;
i = i * 5 + 1 + perturb; i = i * 5 + 1 + perturb;
entry = &table[i & mask]; entry = &table[i & mask];
if (entry->key == NULL) if (entry->key == NULL)
break; goto found_null;
} }
found_null: found_null:
return freeslot == NULL ? entry : freeslot; return freeslot == NULL ? entry : freeslot;
@ -198,17 +208,21 @@ set_insert_clean(PySetObject *so, PyObject *key, Py_hash_t hash)
size_t perturb = hash; size_t perturb = hash;
size_t mask = (size_t)so->mask; size_t mask = (size_t)so->mask;
size_t i = (size_t)hash; size_t i = (size_t)hash;
#if LINEAR_PROBES
size_t j; size_t j;
#endif
while (1) { while (1) {
entry = &table[i & mask]; entry = &table[i & mask];
if (entry->key == NULL) if (entry->key == NULL)
goto found_null; goto found_null;
#if LINEAR_PROBES
for (j = 1 ; j <= LINEAR_PROBES ; j++) { for (j = 1 ; j <= LINEAR_PROBES ; j++) {
entry = &table[(i + j) & mask]; entry = &table[(i + j) & mask];
if (entry->key == NULL) if (entry->key == NULL)
goto found_null; goto found_null;
} }
#endif
perturb >>= PERTURB_SHIFT; perturb >>= PERTURB_SHIFT;
i = i * 5 + 1 + perturb; i = i * 5 + 1 + perturb;
} }