Patchwork [5,of,6] dirs: use mutable integers internally

login
register
mail settings
Submitter Bryan O'Sullivan
Date April 1, 2013, 8:49 p.m.
Message ID <34666bf45a79c688f24f.1364849358@australite.local>
Download mbox | patch
Permalink /patch/1235/
State Accepted, archived
Headers show

Comments

Bryan O'Sullivan - April 1, 2013, 8:49 p.m.
# HG changeset patch
# User Bryan O'Sullivan <bryano@fb.com>
# Date 1364849265 25200
#      Mon Apr 01 13:47:45 2013 -0700
# Node ID 34666bf45a79c688f24fe15ce6a73894a6adfee6
# Parent  71ac3e4fd231239cf5c281cf49ff2fb78b883ce8
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

Patch

diff --git a/mercurial/dirs.c b/mercurial/dirs.c
--- a/mercurial/dirs.c
+++ b/mercurial/dirs.c
@@ -11,6 +11,13 @@ 
 #include <Python.h>
 #include "util.h"
 
+/*
+ * A few implementation notes:
+ *
+ * We modify Python integers for refcounting, but those integers are
+ * never visible to Python code.
+ */
+
 typedef struct {
 	PyObject_HEAD
 	PyObject *dict;
@@ -32,12 +39,11 @@  static inline Py_ssize_t _finddir(PyObje
 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);
 
@@ -45,25 +51,29 @@  static int _addpath(PyObject *dirs, PyOb
 			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;
 }
@@ -71,12 +81,11 @@  bail:
 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);
 
@@ -89,29 +98,16 @@  static int _delpath(PyObject *dirs, PyOb
 					"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;
 }