mirror of https://github.com/python/cpython
Issue 18771: Make it possible to set the number linear probes at compile-time.
This commit is contained in:
parent
e6d35dba3c
commit
8408dc581e
|
@ -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`.)
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue