mirror of
https://github.com/python/cpython.git
synced 2025-12-04 16:43:27 +00:00
Aggressive reordering of dict comparisons. In case of collision, it stands
to reason that me_key is much more likely to match the key we're looking for than to match dummy, and if the key is absent me_key is much more likely to be NULL than dummy: most dicts don't even have a dummy entry. Running instrumented dict code over the test suite and some apps confirmed that matching dummy was 200-300x less frequent than matching key in practice. So this reorders the tests to try the common case first. It can lose if a large dict with many collisions is mostly deleted, not resized, and then frequently searched, but that's hardly a case we should be favoring.
This commit is contained in:
parent
2f228e75e4
commit
342c65e19a
1 changed files with 21 additions and 30 deletions
|
|
@ -219,26 +219,21 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
|
||||||
incr = (hash ^ ((unsigned long)hash >> 3)) & mask;
|
incr = (hash ^ ((unsigned long)hash >> 3)) & mask;
|
||||||
if (!incr)
|
if (!incr)
|
||||||
incr = mask;
|
incr = mask;
|
||||||
|
/* In the loop, me_key == dummy is by far (factor of 100s) the
|
||||||
|
least likely outcome, so test for that last. */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ep = &ep0[(i+incr)&mask];
|
ep = &ep0[(i+incr)&mask];
|
||||||
if (ep->me_key == NULL) {
|
if (ep->me_key == NULL) {
|
||||||
if (restore_error)
|
if (restore_error)
|
||||||
PyErr_Restore(err_type, err_value, err_tb);
|
PyErr_Restore(err_type, err_value, err_tb);
|
||||||
if (freeslot != NULL)
|
return freeslot == NULL ? ep : freeslot;
|
||||||
return freeslot;
|
|
||||||
else
|
|
||||||
return ep;
|
|
||||||
}
|
}
|
||||||
if (ep->me_key == dummy) {
|
if (ep->me_key == key) {
|
||||||
if (freeslot == NULL)
|
|
||||||
freeslot = ep;
|
|
||||||
}
|
|
||||||
else if (ep->me_key == key) {
|
|
||||||
if (restore_error)
|
if (restore_error)
|
||||||
PyErr_Restore(err_type, err_value, err_tb);
|
PyErr_Restore(err_type, err_value, err_tb);
|
||||||
return ep;
|
return ep;
|
||||||
}
|
}
|
||||||
else if (ep->me_hash == hash) {
|
else if (ep->me_hash == hash && ep->me_key != dummy) {
|
||||||
if (!checked_error) {
|
if (!checked_error) {
|
||||||
checked_error = 1;
|
checked_error = 1;
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
|
|
@ -257,11 +252,12 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
|
||||||
else if (cmp < 0)
|
else if (cmp < 0)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
|
else if (ep->me_key == dummy && freeslot == NULL)
|
||||||
|
freeslot = ep;
|
||||||
/* Cycle through GF(2^n)-{0} */
|
/* Cycle through GF(2^n)-{0} */
|
||||||
incr = incr << 1;
|
incr <<= 1;
|
||||||
if (incr > mask)
|
if (incr > mask)
|
||||||
incr ^= mp->ma_poly; /* This will implicitly clear
|
incr ^= mp->ma_poly; /* clears the highest bit */
|
||||||
the highest bit */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,28 +310,23 @@ lookdict_string(dictobject *mp, PyObject *key, register long hash)
|
||||||
incr = (hash ^ ((unsigned long)hash >> 3)) & mask;
|
incr = (hash ^ ((unsigned long)hash >> 3)) & mask;
|
||||||
if (!incr)
|
if (!incr)
|
||||||
incr = mask;
|
incr = mask;
|
||||||
|
/* In the loop, me_key == dummy is by far (factor of 100s) the
|
||||||
|
least likely outcome, so test for that last. */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ep = &ep0[(i+incr)&mask];
|
ep = &ep0[(i+incr)&mask];
|
||||||
if (ep->me_key == NULL) {
|
if (ep->me_key == NULL)
|
||||||
if (freeslot != NULL)
|
return freeslot == NULL ? ep : freeslot;
|
||||||
return freeslot;
|
if (ep->me_key == key
|
||||||
else
|
|| (ep->me_hash == hash
|
||||||
return ep;
|
&& ep->me_key != dummy
|
||||||
}
|
&& compare(ep->me_key, key) == 0))
|
||||||
if (ep->me_key == dummy) {
|
|
||||||
if (freeslot == NULL)
|
|
||||||
freeslot = ep;
|
|
||||||
}
|
|
||||||
else if (ep->me_key == key
|
|
||||||
|| (ep->me_hash == hash
|
|
||||||
&& compare(ep->me_key, key) == 0)) {
|
|
||||||
return ep;
|
return ep;
|
||||||
}
|
else if (ep->me_key == dummy && freeslot == NULL)
|
||||||
|
freeslot = ep;
|
||||||
/* Cycle through GF(2^n)-{0} */
|
/* Cycle through GF(2^n)-{0} */
|
||||||
incr = incr << 1;
|
incr <<= 1;
|
||||||
if (incr > mask)
|
if (incr > mask)
|
||||||
incr ^= mp->ma_poly; /* This will implicitly clear
|
incr ^= mp->ma_poly; /* clears the highest bit */
|
||||||
the highest bit */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue