cinap_lenrekadd hg and python4bb9e2215 years agoHistory
2486 lines · 62.9 KB · C
RawDownload
 /* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt,   describing explorations into dictionary design and optimization.   It covers typical dictionary use patterns, the parameters for   tuning dictionaries, and several ideas for possible optimizations.*/ #include "Python.h" typedef PyDictEntry dictentry;typedef PyDictObject dictobject; /* Set a key error with the specified argument, wrapping it in a * tuple automatically so that tuple keys are not unpacked as the * exception arguments. */static voidset_key_error(PyObject *arg){	PyObject *tup;	tup = PyTuple_Pack(1, arg);	if (!tup)		return; /* caller will expect error to be set anyway */	PyErr_SetObject(PyExc_KeyError, tup);	Py_DECREF(tup);} /* Define this out if you don't want conversion statistics on exit. */#undef SHOW_CONVERSION_COUNTS /* See large comment block below.  This must be >= 1. */#define PERTURB_SHIFT 5 /*Major subtleties ahead:  Most hash schemes depend on having a "good" hashfunction, in the sense of simulating randomness.  Python doesn't:  its mostimportant hash functions (for strings and ints) are very regular in commoncases: >>> map(hash, (0, 1, 2, 3))[0, 1, 2, 3]>>> map(hash, ("namea", "nameb", "namec", "named"))[-1658398457, -1658398460, -1658398459, -1658398462]>>> This isn't necessarily bad!  To the contrary, in a table of size 2**i, takingthe low-order i bits as the initial table index is extremely fast, and thereare no collisions at all for dicts indexed by a contiguous range of ints.The same is approximately true when keys are "consecutive" strings.  So thisgives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of thehash table makes a good collision resolution strategy crucial.  Taking onlythe last i bits of the hash code is also vulnerable:  for example, consider[i << 16 for i in range(20000)] as a set of keys.  Since ints are their ownhash codes, and this fits in a dict of size 2**15, the last 15 bits of everyhash code are all 0:  they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just takethe last i bits anyway.  It's up to collision resolution to do the rest.  Ifwe *usually* find the key we're looking for on the first try (and, it turnsout, we usually do -- the table load factor is kept under 2/3, so the oddsare solidly in our favor), then it makes best sense to keep the initial indexcomputation dirt cheap. The first half of collision resolution is to visit table indices via thisrecurrence:     j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates eachint in range(2**i) exactly once (see any text on random-number generation forproof).  By itself, this doesn't help much:  like linear probing (settingj += 1, or j -= 1, on each loop trip), it scans the table entries in a fixedorder.  This would be bad, except that's not the only thing we do, and it'sactually *good* in the common cases where hash keys are consecutive.  In anexample that's really too small to make this entirely clear, for a table ofsize 2**3 the order of indices is:     0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2,not 6, so if another comes in at index 6 the collision at 5 didn't hurt it.Linear probing is deadly in this case because there the fixed probe orderis the *same* as the order consecutive keys are likely to arrive.  But it'sextremely unlikely hash codes will follow a 5*j+1 recurrence by accident,and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash codeinto play.  This is done by initializing a (unsigned) vrbl "perturb" to thefull hash code, and changing the recurrence to:     j = (5*j) + 1 + perturb;    perturb >>= PERTURB_SHIFT;    use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code,and the pseudo-scrambling property of recurring on 5*j+1 is more valuable,because it quickly magnifies small differences in the bits that didn't affectthe initial index.  Note that because perturb is unsigned, if the recurrenceis executed often enough perturb eventually becomes and remains 0.  At thatpoint (very rarely reached) the recurrence is on (just) 5*j+1 again, andthat's certain to find an empty slot eventually (since it generates every intin range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act.  You want itsmall so that the high bits of the hash code continue to affect the probesequence across iterations; but you want it large so that in really bad casesthe high-order hash bits have an effect on early iterations.  5 was "thebest" in minimizing total collisions across experiments Tim Peters ran (onboth normal and pathological cases), but 4 and 6 weren't significantly worse. Historical:  Reimer Behrends contributed the idea of using a polynomial-basedapproach, using repeated multiplication by x in GF(2**n) where an irreduciblepolynomial for each table size was chosen such that x was a primitive root.Christian Tismer later extended that to use division by x instead, as anefficient way to get the high bits of the hash code into play.  This schemealso gave excellent collision statistics, but was more expensive:  twoif-tests were required inside the loop; computing "the next" index took aboutthe same number of operations but without as much potential parallelism(e.g., computing 5*j can go on at the same time as computing 1+perturb in theabove, and then shifting perturb can be done while the table index is beingmasked); and the dictobject struct required a member to hold the table'spolynomial.  In Tim's experiments the current scheme ran faster, producedequally good collision statistics, needed less code & used less memory. Theoretical Python 2.5 headache:  hash codes are only C "long", butsizeof(Py_ssize_t) > sizeof(long) may be possible.  In that case, and if adict is genuinely huge, then only the slots directly reachable via indexingby a C long can be the first slot in a probe sequence.  The probe sequencewill still eventually reach every slot in the table, but the collision rateon initial probes may be much higher than this scheme was designed for.Getting a hash code as fat as Py_ssize_t is the only real cure.  But inpractice, this probably won't make a lick of difference for many years (atwhich point everyone will have terabytes of RAM on 64-bit boxes).*/ /* Object used as dummy key to fill deleted entries */static PyObject *dummy = NULL; /* Initialized by first call to newdictobject() */ #ifdef Py_REF_DEBUGPyObject *_PyDict_Dummy(void){	return dummy;}#endif /* forward declarations */static dictentry *lookdict_string(dictobject *mp, PyObject *key, long hash); #ifdef SHOW_CONVERSION_COUNTSstatic long created = 0L;static long converted = 0L; static voidshow_counts(void){	fprintf(stderr, "created %ld string dicts\n", created);	fprintf(stderr, "converted %ld to normal dicts\n", converted);	fprintf(stderr, "%.2f%% conversion rate\n", (100.0*converted)/created);}#endif /* Initialization macros.   There are two ways to create a dict:  PyDict_New() is the main C API   function, and the tp_new slot maps to dict_new().  In the latter case we   can save a little time over what PyDict_New does because it's guaranteed   that the PyDictObject struct is already zeroed out.   Everyone except dict_new() should use EMPTY_TO_MINSIZE (unless they have   an excellent reason not to).*/ #define INIT_NONZERO_DICT_SLOTS(mp) do {				\	(mp)->ma_table = (mp)->ma_smalltable;				\	(mp)->ma_mask = PyDict_MINSIZE - 1;				\    } while(0) #define EMPTY_TO_MINSIZE(mp) do {					\	memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable));	\	(mp)->ma_used = (mp)->ma_fill = 0;				\	INIT_NONZERO_DICT_SLOTS(mp);					\    } while(0) /* Dictionary reuse scheme to save calls to malloc, free, and memset */#define MAXFREEDICTS 80static PyDictObject *free_dicts[MAXFREEDICTS];static int num_free_dicts = 0; PyObject *PyDict_New(void){	register dictobject *mp;	if (dummy == NULL) { /* Auto-initialize dummy */		dummy = PyString_FromString("<dummy key>");		if (dummy == NULL)			return NULL;#ifdef SHOW_CONVERSION_COUNTS		Py_AtExit(show_counts);#endif	}	if (num_free_dicts) {		mp = free_dicts[--num_free_dicts];		assert (mp != NULL);		assert (mp->ob_type == &PyDict_Type);		_Py_NewReference((PyObject *)mp);		if (mp->ma_fill) {			EMPTY_TO_MINSIZE(mp);		}		assert (mp->ma_used == 0);		assert (mp->ma_table == mp->ma_smalltable);		assert (mp->ma_mask == PyDict_MINSIZE - 1);	} else {		mp = PyObject_GC_New(dictobject, &PyDict_Type);		if (mp == NULL)			return NULL;		EMPTY_TO_MINSIZE(mp);	}	mp->ma_lookup = lookdict_string;#ifdef SHOW_CONVERSION_COUNTS	++created;#endif	_PyObject_GC_TRACK(mp);	return (PyObject *)mp;} /*The basic lookup function used by all operations.This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.Open addressing is preferred over chaining since the link overhead forchaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequentprobe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. (The details in this version are due to Tim Peters, building on many pastcontributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov andChristian Tismer). lookdict() is general-purpose, and may return NULL if (and only if) acomparison raises an exception (this was new in Python 2.5).lookdict_string() below is specialized to string keys, comparison of which cannever raise an exception; that function can never return NULL.  For both, whenthe key isn't found a dictentry* is returned for which the me_value field isNULL; this is the slot in the dict at which the key would have been found, andthe caller can (if it wishes) add the <key, value> pair to the returneddictentry*.*/static dictentry *lookdict(dictobject *mp, PyObject *key, register long hash){	register size_t i;	register size_t perturb;	register dictentry *freeslot;	register size_t mask = (size_t)mp->ma_mask;	dictentry *ep0 = mp->ma_table;	register dictentry *ep;	register int cmp;	PyObject *startkey; 	i = (size_t)hash & mask;	ep = &ep0[i];	if (ep->me_key == NULL || ep->me_key == key)		return ep; 	if (ep->me_key == dummy)		freeslot = ep;	else {		if (ep->me_hash == hash) {			startkey = ep->me_key;			cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);			if (cmp < 0)				return NULL;			if (ep0 == mp->ma_table && ep->me_key == startkey) {				if (cmp > 0)					return ep;			}			else {				/* The compare did major nasty stuff to the				 * dict:  start over.				 * XXX A clever adversary could prevent this				 * XXX from terminating. 				 */ 				return lookdict(mp, key, hash); 			}		}		freeslot = NULL;	} 	/* In the loop, me_key == dummy is by far (factor of 100s) the	   least likely outcome, so test for that last. */	for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {		i = (i << 2) + i + perturb + 1;		ep = &ep0[i & mask];		if (ep->me_key == NULL)			return freeslot == NULL ? ep : freeslot;		if (ep->me_key == key)			return ep;		if (ep->me_hash == hash && ep->me_key != dummy) {			startkey = ep->me_key;			cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);			if (cmp < 0)				return NULL;			if (ep0 == mp->ma_table && ep->me_key == startkey) {				if (cmp > 0)					return ep;			}			else {				/* The compare did major nasty stuff to the				 * dict:  start over.				 * XXX A clever adversary could prevent this				 * XXX from terminating. 				 */ 				return lookdict(mp, key, hash); 			}		}		else if (ep->me_key == dummy && freeslot == NULL)			freeslot = ep;	}	assert(0);	/* NOT REACHED */	return 0;} /* * Hacked up version of lookdict which can assume keys are always strings; * this assumption allows testing for errors during PyObject_RichCompareBool() * to be dropped; string-string comparisons never raise exceptions.  This also * means we don't need to go through PyObject_RichCompareBool(); we can always * use _PyString_Eq() directly. * * This is valuable because dicts with only string keys are very common. */static dictentry *lookdict_string(dictobject *mp, PyObject *key, register long hash){	register size_t i;	register size_t perturb;	register dictentry *freeslot;	register size_t mask = (size_t)mp->ma_mask;	dictentry *ep0 = mp->ma_table;	register dictentry *ep; 	/* Make sure this function doesn't have to handle non-string keys,	   including subclasses of str; e.g., one reason to subclass	   strings is to override __eq__, and for speed we don't cater to	   that here. */	if (!PyString_CheckExact(key)) {#ifdef SHOW_CONVERSION_COUNTS		++converted;#endif		mp->ma_lookup = lookdict;		return lookdict(mp, key, hash);	}	i = hash & mask;	ep = &ep0[i];	if (ep->me_key == NULL || ep->me_key == key)		return ep;	if (ep->me_key == dummy)		freeslot = ep;	else {		if (ep->me_hash == hash && _PyString_Eq(ep->me_key, key))			return ep;		freeslot = NULL;	} 	/* In the loop, me_key == dummy is by far (factor of 100s) the	   least likely outcome, so test for that last. */	for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {		i = (i << 2) + i + perturb + 1;		ep = &ep0[i & mask];		if (ep->me_key == NULL)			return freeslot == NULL ? ep : freeslot;		if (ep->me_key == key		    || (ep->me_hash == hash		        && ep->me_key != dummy			&& _PyString_Eq(ep->me_key, key)))			return ep;		if (ep->me_key == dummy && freeslot == NULL)			freeslot = ep;	}	assert(0);	/* NOT REACHED */	return 0;} /*Internal routine to insert a new item into the table.Used both by the internal resize routine and by the public insert routine.Eats a reference to key and one to value.Returns -1 if an error occurred, or 0 on success.*/static intinsertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value){	PyObject *old_value;	register dictentry *ep;	typedef PyDictEntry *(*lookupfunc)(PyDictObject *, PyObject *, long); 	assert(mp->ma_lookup != NULL);	ep = mp->ma_lookup(mp, key, hash);	if (ep == NULL) {		Py_DECREF(key);		Py_DECREF(value);		return -1;	}	if (ep->me_value != NULL) {		old_value = ep->me_value;		ep->me_value = value;		Py_DECREF(old_value); /* which **CAN** re-enter */		Py_DECREF(key);	}	else {		if (ep->me_key == NULL)			mp->ma_fill++;		else {			assert(ep->me_key == dummy);			Py_DECREF(dummy);		}		ep->me_key = key;		ep->me_hash = (Py_ssize_t)hash;		ep->me_value = value;		mp->ma_used++;	}	return 0;} /*Internal routine used by dictresize() to insert an item which isknown to be absent from the dict.  This routine also assumes thatthe dict contains no deleted entries.  Besides the performance benefit,using insertdict() in dictresize() is dangerous (SF bug #1456209).Note that no refcounts are changed by this routine; if needed, the calleris responsible for incref'ing `key` and `value`.*/static voidinsertdict_clean(register dictobject *mp, PyObject *key, long hash,		 PyObject *value){	register size_t i;	register size_t perturb;	register size_t mask = (size_t)mp->ma_mask;	dictentry *ep0 = mp->ma_table;	register dictentry *ep; 	i = hash & mask;	ep = &ep0[i];	for (perturb = hash; ep->me_key != NULL; perturb >>= PERTURB_SHIFT) {		i = (i << 2) + i + perturb + 1;		ep = &ep0[i & mask];	}	assert(ep->me_value == NULL);	mp->ma_fill++;	ep->me_key = key;	ep->me_hash = (Py_ssize_t)hash;	ep->me_value = value;	mp->ma_used++;} /*Restructure the table by allocating a new table and reinserting allitems again.  When entries have been deleted, the new table mayactually be smaller than the old one.*/static intdictresize(dictobject *mp, Py_ssize_t minused){	Py_ssize_t newsize;	dictentry *oldtable, *newtable, *ep;	Py_ssize_t i;	int is_oldtable_malloced;	dictentry small_copy[PyDict_MINSIZE]; 	assert(minused >= 0); 	/* Find the smallest table size > minused. */	for (newsize = PyDict_MINSIZE;	     newsize <= minused && newsize > 0;	     newsize <<= 1)		;	if (newsize <= 0) {		PyErr_NoMemory();		return -1;	} 	/* Get space for a new table. */	oldtable = mp->ma_table;	assert(oldtable != NULL);	is_oldtable_malloced = oldtable != mp->ma_smalltable; 	if (newsize == PyDict_MINSIZE) {		/* A large table is shrinking, or we can't get any smaller. */		newtable = mp->ma_smalltable;		if (newtable == oldtable) {			if (mp->ma_fill == mp->ma_used) {				/* No dummies, so no point doing anything. */				return 0;			}

Lines 1–500 of 2486.