summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2013-09-01 04:27:08 (GMT)
committerRaymond Hettinger <python@rcn.com>2013-09-01 04:27:08 (GMT)
commit95c0d67581a9fb4ef94213871d6574e9db4114a4 (patch)
tree9eb62290b5e480aefeba9ee9d2f6cd8bed55b1aa /Objects
parent34567ec94b17e159fac8424559698f48a713991f (diff)
downloadcpython-95c0d67581a9fb4ef94213871d6574e9db4114a4.zip
cpython-95c0d67581a9fb4ef94213871d6574e9db4114a4.tar.gz
cpython-95c0d67581a9fb4ef94213871d6574e9db4114a4.tar.bz2
Further reduce the cost of hash collisions by inspecting an additional nearby entry.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/setobject.c43
1 files changed, 39 insertions, 4 deletions
diff --git a/Objects/setobject.c b/Objects/setobject.c
index 98969f5..51a1653 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -65,10 +65,11 @@ chaining would be substantial (100% with typical malloc overhead).
The initial probe index is computed as hash mod the table size. Subsequent
probe indices are computed as explained in Objects/dictobject.c.
-To improve cache locality, each probe is done in pairs.
-After the probe is examined, an adjacent entry is then examined as well.
-The likelihood is that an adjacent entry is in the same cache line and
-can be examined more cheaply than another probe elsewhere in memory.
+To improve cache locality, each probe inspects nearby entries before
+moving on to probes elsewhere in memory. Depending on alignment and the
+size of a cache line, the nearby entries are cheaper to inspect than
+other probes elsewhere in memory. This probe strategy reduces the cost
+of hash collisions.
All arithmetic on hash should ignore overflow.
@@ -130,6 +131,26 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
if (entry->key == dummy && freeslot == NULL)
freeslot = entry;
+ entry = &table[j ^ 2];
+ if (entry->key == NULL)
+ break;
+ if (entry->key == key)
+ return entry;
+ if (entry->hash == hash && entry->key != dummy) {
+ PyObject *startkey = entry->key;
+ Py_INCREF(startkey);
+ cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
+ Py_DECREF(startkey);
+ if (cmp < 0)
+ return NULL;
+ if (table != so->table || entry->key != startkey)
+ return set_lookkey(so, key, hash);
+ if (cmp > 0)
+ return entry;
+ }
+ if (entry->key == dummy && freeslot == NULL)
+ freeslot = entry;
+
i = i * 5 + perturb + 1;
j = i & mask;
perturb >>= PERTURB_SHIFT;
@@ -190,6 +211,17 @@ set_lookkey_unicode(PySetObject *so, PyObject *key, Py_hash_t hash)
if (entry->key == dummy && freeslot == NULL)
freeslot = entry;
+ entry = &table[j ^ 2];
+ if (entry->key == NULL)
+ break;
+ if (entry->key == key
+ || (entry->hash == hash
+ && entry->key != dummy
+ && unicode_eq(entry->key, key)))
+ return entry;
+ if (entry->key == dummy && freeslot == NULL)
+ freeslot = entry;
+
i = i * 5 + perturb + 1;
j = i & mask;
perturb >>= PERTURB_SHIFT;
@@ -258,6 +290,9 @@ set_insert_clean(PySetObject *so, PyObject *key, Py_hash_t hash)
entry = &table[j ^ 1];
if (entry->key == NULL)
break;
+ entry = &table[j ^ 2];
+ if (entry->key == NULL)
+ break;
i = i * 5 + perturb + 1;
j = i & mask;
perturb >>= PERTURB_SHIFT;