revlog: switch to a C version of headrevs
The C implementation is more than 100 times faster than the Python
version (which is still available as a fallback).
In a repo with 330,000 revs and a stale .hg/cache/tags file, this
patch improves the performance of "hg tip" from 2.2 to 1.6 seconds.
--- a/mercurial/parsers.c Sat May 19 19:44:23 2012 -0700
+++ b/mercurial/parsers.c Sat May 19 19:44:58 2012 -0700
@@ -534,6 +534,81 @@
return NULL;
}
+static PyObject *index_headrevs(indexObject *self)
+{
+ Py_ssize_t i, len, addlen;
+ char *nothead = NULL;
+ PyObject *heads;
+
+ len = index_length(self) - 1;
+ heads = PyList_New(0);
+ if (heads == NULL)
+ goto bail;
+ if (len == 0) {
+ PyObject *nullid = PyInt_FromLong(-1);
+ if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
+ Py_XDECREF(nullid);
+ goto bail;
+ }
+ goto done;
+ }
+
+ nothead = calloc(len, 1);
+ if (nothead == NULL)
+ goto bail;
+
+ for (i = 0; i < self->raw_length; i++) {
+ const char *data = index_deref(self, i);
+ int parent_1 = getbe32(data + 24);
+ int parent_2 = getbe32(data + 28);
+ if (parent_1 >= 0)
+ nothead[parent_1] = 1;
+ if (parent_2 >= 0)
+ nothead[parent_2] = 1;
+ }
+
+ addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
+
+ for (i = 0; i < addlen; i++) {
+ PyObject *rev = PyList_GET_ITEM(self->added, i);
+ PyObject *p1 = PyTuple_GET_ITEM(rev, 5);
+ PyObject *p2 = PyTuple_GET_ITEM(rev, 6);
+ long parent_1, parent_2;
+
+ if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
+ PyErr_SetString(PyExc_TypeError,
+ "revlog parents are invalid");
+ goto bail;
+ }
+ parent_1 = PyInt_AS_LONG(p1);
+ parent_2 = PyInt_AS_LONG(p2);
+ if (parent_1 >= 0)
+ nothead[parent_1] = 1;
+ if (parent_2 >= 0)
+ nothead[parent_2] = 1;
+ }
+
+ for (i = 0; i < len; i++) {
+ PyObject *head;
+
+ if (nothead[i])
+ continue;
+ head = PyInt_FromLong(i);
+ if (head == NULL || PyList_Append(heads, head) == -1) {
+ Py_XDECREF(head);
+ goto bail;
+ }
+ }
+
+done:
+ free(nothead);
+ return heads;
+bail:
+ Py_XDECREF(heads);
+ free(nothead);
+ return NULL;
+}
+
static inline int nt_level(const char *node, Py_ssize_t level)
{
int v = node[level>>1];
@@ -1144,6 +1219,8 @@
"clear the index caches"},
{"get", (PyCFunction)index_m_get, METH_VARARGS,
"get an index entry"},
+ {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS,
+ "get head revisions"},
{"insert", (PyCFunction)index_insert, METH_VARARGS,
"insert an index entry"},
{"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
--- a/mercurial/revlog.py Sat May 19 19:44:23 2012 -0700
+++ b/mercurial/revlog.py Sat May 19 19:44:58 2012 -0700
@@ -635,6 +635,10 @@
return (orderedout, roots, heads)
def headrevs(self):
+ try:
+ return self.index.headrevs()
+ except AttributeError:
+ pass
count = len(self)
if not count:
return [nullrev]