Skip to content

Commit 9f1a679

Browse files
committed
Revised the set() and frozenset() implementaion to use its own internal
data structure instead of using dictionaries. Reduces memory consumption by 1/3 and provides modest speed-ups for most set operations.
1 parent fe25643 commit 9f1a679

3 files changed

Lines changed: 912 additions & 224 deletions

File tree

Include/setobject.h

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/* Set object interface */
32

43
#ifndef Py_SETOBJECT_H
@@ -7,28 +6,61 @@
76
extern "C" {
87
#endif
98

9+
1010
/*
11-
This data structure is shared by set and frozenset objects.
11+
There are three kinds of slots in the table:
12+
13+
1. Unused: key == NULL
14+
2. Active: key != NULL and key != dummy
15+
3. Dummy: key == dummy
1216
*/
1317

18+
#define PySet_MINSIZE 8
19+
1420
typedef struct {
21+
long hash; /* cached hash code for the entry key */
22+
PyObject *key;
23+
} setentry;
24+
25+
26+
/*
27+
This data structure is shared by set and frozenset objects.
28+
*/
29+
30+
typedef struct _setobject PySetObject;
31+
struct _setobject {
1532
PyObject_HEAD
16-
PyObject *data;
17-
long hash; /* only used by frozenset objects */
18-
PyObject *weakreflist; /* List of weak references */
19-
20-
/* Invariants:
21-
* data is a dictionary whose values are all True.
22-
* data points to the same dict for the whole life of the set.
23-
* For frozensets only:
24-
* data is immutable.
25-
* hash is the hash of the frozenset or -1 if not computed yet.
33+
34+
int fill; /* # Active + # Dummy */
35+
int used; /* # Active */
36+
37+
/* The table contains mask + 1 slots, and that's a power of 2.
38+
* We store the mask instead of the size because the mask is more
39+
* frequently needed.
2640
*/
27-
} PySetObject;
41+
int mask;
42+
43+
/* table points to smalltable for small tables, else to
44+
* additional malloc'ed memory. table is never NULL! This rule
45+
* saves repeated runtime null-tests in the workhorse getitem and
46+
* setitem calls.
47+
*/
48+
setentry *table;
49+
setentry *(*lookup)(PySetObject *so, PyObject *key, long hash);
50+
setentry smalltable[PySet_MINSIZE];
51+
52+
long hash; /* only used by frozenset objects */
53+
PyObject *weakreflist; /* List of weak references */
54+
};
2855

2956
PyAPI_DATA(PyTypeObject) PySet_Type;
3057
PyAPI_DATA(PyTypeObject) PyFrozenSet_Type;
3158

59+
/* Invariants for frozensets only:
60+
* data is immutable.
61+
* hash is the hash of the frozenset or -1 if not computed yet.
62+
*/
63+
3264
#define PyFrozenSet_CheckExact(ob) ((ob)->ob_type == &PyFrozenSet_Type)
3365
#define PyAnySet_Check(ob) \
3466
((ob)->ob_type == &PySet_Type || (ob)->ob_type == &PyFrozenSet_Type || \

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ What's New in Python 2.5 alpha 1?
1212
Core and builtins
1313
-----------------
1414

15+
- The implementation of set() and frozenset() was revised to use its
16+
own internal data structure. Memory consumption is reduced by 1/3
17+
and there are modest speed-ups as well. The API is unchanged.
18+
1519
- SF bug #1238681: freed pointer is used in longobject.c:long_pow().
1620

1721
- SF bug #1229429: PyObject_CallMethod failed to decrement some

0 commit comments

Comments
 (0)