[PATCH 6 of 7 V4] delta: have a native implementation of _findsnapshot

Yuya Nishihara yuya at tcha.org
Wed Jan 2 03:04:05 UTC 2019


On Sun, 30 Dec 2018 19:43:53 +0100, Boris Feld wrote:
> # HG changeset patch
> # User Boris Feld <boris.feld at octobus.net>
> # Date 1545297320 -3600
> #      Thu Dec 20 10:15:20 2018 +0100
> # Node ID a6bb015a9fb1a2c23d73f1c55e7d4160a9701191
> # Parent  5aff42c50dd1bc1e7c2e533a8e052fa42c6f9304
> # EXP-Topic sparse-revlog
> # Available At https://bitbucket.org/octobus/mercurial-devel/
> #              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r a6bb015a9fb1
> delta: have a native implementation of _findsnapshot
> 
> The function might traverse a lot of revision, a native implementation get
> significantly faster.
> 
> example affected manifest write
> before: 0.114989
> after:  0.067141 (-42%)
> 
> diff --git a/mercurial/cext/revlog.c b/mercurial/cext/revlog.c
> --- a/mercurial/cext/revlog.c
> +++ b/mercurial/cext/revlog.c
> @@ -1050,6 +1050,80 @@ static PyObject *index_issnapshot(indexO
>  	return PyBool_FromLong((long)issnap);
>  }
>  
> +static PyObject *index_findsnapshots(indexObject *self, PyObject *args)
> +{
> +
> +	Py_ssize_t start_rev;
> +	PyObject *cache;
> +	Py_ssize_t base;
> +	Py_ssize_t rev;
> +	int issnap;

> +	PyObject *key;
> +	bool newallvalues;
> +	PyObject *allvalues;
> +	PyObject *value;

These variables have to be initialized since we do XDECREF() on failure.

> +	if (!PyArg_ParseTuple(args, "On", &cache, &start_rev)) {
> +		return NULL;
> +	}
> +	if (!PyDict_Check(cache)) {
> +		PyErr_SetString(PyExc_TypeError,
> +		                "cache argument must be a dict");
> +		return NULL;
> +	}

Nit: maybe this can be "O!" with &PyDict_Type.

> +	for (rev = start_rev; rev < length; rev++) {
> +		issnap = index_issnapshotrev(self, rev);
> +		if (issnap < 0) {
> +			goto bail;
> +		}
> +		if (issnap == 0) {
> +			continue;
> +		}
> +		base = (Py_ssize_t)index_baserev(self, rev);
> +		if (base == rev) {
> +			base = -1;
> +		}
> +		if (base == -2) {
> +			assert(PyErr_Occurred());
> +			goto bail;
> +		}
> +		key = PyInt_FromSsize_t(base);
> +		newallvalues = false;
> +		allvalues = PyDict_GetItem(cache, key);
> +		if (allvalues == NULL && PyErr_Occurred()) {
> +			goto bail;
> +		}
> +		if (allvalues == NULL) {
> +			allvalues = PyList_New(0);

PyList_New() may fail in theory.

> +			newallvalues = true;
> +			if (PyDict_SetItem(cache, key, allvalues) < 0) {
> +				goto bail;
> +			}

I prefer cleaning up the allvalues reference here for this particular case,
so that the scope of the allvalues can be narrowed and the newallvalues can
be eliminated.

  for (rev ...) {
      PyObject *allvalues; /* borrowed */
      ...
      if (allvalues == NULL) {
          int r;
          allvalues = PyList_New(0);
          if (!allvalues)
              goto bail;
          r = PyDict_SetItem(cache, key, allvalues);
          Py_DECREF(allvalues);
          if (r < 0)
              goto bail;
      }

> +		Py_XDECREF(key);
> +		if (newallvalues) {
> +			Py_XDECREF(allvalues);
> +		}
> +		Py_XDECREF(value);
> +		key = NULL;
> +		allvalues = NULL;
> +		value = NULL;

Nit: Py_CLEAR() could be used in place of XDECREF() + = NULL.

> +	}
> +	Py_INCREF(Py_None);
> +	return Py_None;

Nit: can be Py_RETURN_NONE;


More information about the Mercurial-devel mailing list