# HG changeset patch # User Bryan O'Sullivan # Date 1365631707 25200 # Node ID 66d3aebe2d95a4380e6eac434bedc7de09426627 # Parent 02ee846b246a08cb4f09816b232fd0aa543394bc dirs: use mutable integers internally These integers are not visible to Python code, so this is safe. perfdirs results for a working dir with 170,000 files: Python 638 msec C 244 C+int 192 diff -r 02ee846b246a -r 66d3aebe2d95 mercurial/dirs.c --- a/mercurial/dirs.c Wed Apr 10 15:08:27 2013 -0700 +++ b/mercurial/dirs.c Wed Apr 10 15:08:27 2013 -0700 @@ -14,6 +14,11 @@ /* * This is a multiset of directory names, built from the files that * appear in a dirstate or manifest. + * + * A few implementation notes: + * + * We modify Python integers for refcounting, but those integers are + * never visible to Python code. */ typedef struct { PyObject_HEAD @@ -36,12 +41,11 @@ static int _addpath(PyObject *dirs, PyObject *path) { Py_ssize_t pos = PyString_GET_SIZE(path); - PyObject *newval = NULL, *key = NULL; + PyObject *key = NULL; int ret = -1; while ((pos = _finddir(path, pos - 1)) != -1) { PyObject *val; - long v = 0; key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos); @@ -49,25 +53,29 @@ goto bail; val = PyDict_GetItem(dirs, key); - if (val != NULL) - v = PyInt_AS_LONG(val); + if (val != NULL) { + PyInt_AS_LONG(val) += 1; + Py_CLEAR(key); + continue; + } - newval = PyInt_FromLong(v + 1); + /* Force Python to not reuse a small shared int. */ + val = PyInt_FromLong(0x1eadbeef); - if (newval == NULL) + if (val == NULL) goto bail; - ret = PyDict_SetItem(dirs, key, newval); + PyInt_AS_LONG(val) = 1; + ret = PyDict_SetItem(dirs, key, val); + Py_DECREF(val); if (ret == -1) goto bail; Py_CLEAR(key); - Py_CLEAR(newval); } ret = 0; bail: Py_XDECREF(key); - Py_XDECREF(newval); return ret; } @@ -75,12 +83,11 @@ static int _delpath(PyObject *dirs, PyObject *path) { Py_ssize_t pos = PyString_GET_SIZE(path); - PyObject *newval = NULL, *key = NULL; + PyObject *key = NULL; int ret = -1; while ((pos = _finddir(path, pos - 1)) != -1) { PyObject *val; - long v; key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos); @@ -93,29 +100,16 @@ "expected a value, found none"); goto bail; } - v = PyInt_AS_LONG(val); - if (v <= 1) { - if (PyDict_DelItem(dirs, key) == -1) - goto bail; - continue; - } - newval = PyInt_FromLong(v - 1); - - if (newval == NULL) - goto bail; - - ret = PyDict_SetItem(dirs, key, newval); - if (ret == -1) + if (--PyInt_AS_LONG(val) <= 0 && + PyDict_DelItem(dirs, key) == -1) goto bail; Py_CLEAR(key); - Py_CLEAR(newval); } ret = 0; bail: Py_XDECREF(key); - Py_XDECREF(newval); return ret; }