##// END OF EJS Templates
dirs: document performance reasons for bypassing Python C API...
Gregory Szorc -
r30107:da08f470 default
parent child Browse files
Show More
@@ -1,304 +1,312
1 /*
1 /*
2 dirs.c - dynamic directory diddling for dirstates
2 dirs.c - dynamic directory diddling for dirstates
3
3
4 Copyright 2013 Facebook
4 Copyright 2013 Facebook
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define PY_SSIZE_T_CLEAN
10 #define PY_SSIZE_T_CLEAN
11 #include <Python.h>
11 #include <Python.h>
12 #include "util.h"
12 #include "util.h"
13
13
14 #ifdef IS_PY3K
14 #ifdef IS_PY3K
15 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[1]
15 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[1]
16 #else
16 #else
17 #define PYLONG_VALUE(o) PyInt_AS_LONG(o)
17 #define PYLONG_VALUE(o) PyInt_AS_LONG(o)
18 #endif
18 #endif
19
19
20 /*
20 /*
21 * This is a multiset of directory names, built from the files that
21 * This is a multiset of directory names, built from the files that
22 * appear in a dirstate or manifest.
22 * appear in a dirstate or manifest.
23 *
23 *
24 * A few implementation notes:
24 * A few implementation notes:
25 *
25 *
26 * We modify Python integers for refcounting, but those integers are
26 * We modify Python integers for refcounting, but those integers are
27 * never visible to Python code.
27 * never visible to Python code.
28 *
28 *
29 * We mutate strings in-place, but leave them immutable once they can
29 * We mutate strings in-place, but leave them immutable once they can
30 * be seen by Python code.
30 * be seen by Python code.
31 */
31 */
32 typedef struct {
32 typedef struct {
33 PyObject_HEAD
33 PyObject_HEAD
34 PyObject *dict;
34 PyObject *dict;
35 } dirsObject;
35 } dirsObject;
36
36
37 static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
37 static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
38 {
38 {
39 while (pos != -1) {
39 while (pos != -1) {
40 if (path[pos] == '/')
40 if (path[pos] == '/')
41 break;
41 break;
42 pos -= 1;
42 pos -= 1;
43 }
43 }
44
44
45 return pos;
45 return pos;
46 }
46 }
47
47
48 static int _addpath(PyObject *dirs, PyObject *path)
48 static int _addpath(PyObject *dirs, PyObject *path)
49 {
49 {
50 const char *cpath = PyBytes_AS_STRING(path);
50 const char *cpath = PyBytes_AS_STRING(path);
51 Py_ssize_t pos = PyBytes_GET_SIZE(path);
51 Py_ssize_t pos = PyBytes_GET_SIZE(path);
52 PyObject *key = NULL;
52 PyObject *key = NULL;
53 int ret = -1;
53 int ret = -1;
54
54
55 /* This loop is super critical for performance. That's why we inline
56 * access to Python structs instead of going through a supported API.
57 * The implementation, therefore, is heavily dependent on CPython
58 * implementation details. We also commit violations of the Python
59 * "protocol" such as mutating immutable objects. But since we only
60 * mutate objects created in this function or in other well-defined
61 * locations, the references are known so these violations should go
62 * unnoticed. */
55 while ((pos = _finddir(cpath, pos - 1)) != -1) {
63 while ((pos = _finddir(cpath, pos - 1)) != -1) {
56 PyObject *val;
64 PyObject *val;
57
65
58 /* It's likely that every prefix already has an entry
66 /* It's likely that every prefix already has an entry
59 in our dict. Try to avoid allocating and
67 in our dict. Try to avoid allocating and
60 deallocating a string for each prefix we check. */
68 deallocating a string for each prefix we check. */
61 if (key != NULL)
69 if (key != NULL)
62 ((PyBytesObject *)key)->ob_shash = -1;
70 ((PyBytesObject *)key)->ob_shash = -1;
63 else {
71 else {
64 /* Force Python to not reuse a small shared string. */
72 /* Force Python to not reuse a small shared string. */
65 key = PyBytes_FromStringAndSize(cpath,
73 key = PyBytes_FromStringAndSize(cpath,
66 pos < 2 ? 2 : pos);
74 pos < 2 ? 2 : pos);
67 if (key == NULL)
75 if (key == NULL)
68 goto bail;
76 goto bail;
69 }
77 }
70 Py_SIZE(key) = pos;
78 Py_SIZE(key) = pos;
71 ((PyBytesObject *)key)->ob_sval[pos] = '\0';
79 ((PyBytesObject *)key)->ob_sval[pos] = '\0';
72
80
73 val = PyDict_GetItem(dirs, key);
81 val = PyDict_GetItem(dirs, key);
74 if (val != NULL) {
82 if (val != NULL) {
75 PYLONG_VALUE(val) += 1;
83 PYLONG_VALUE(val) += 1;
76 break;
84 break;
77 }
85 }
78
86
79 /* Force Python to not reuse a small shared int. */
87 /* Force Python to not reuse a small shared int. */
80 #ifdef IS_PY3K
88 #ifdef IS_PY3K
81 val = PyLong_FromLong(0x1eadbeef);
89 val = PyLong_FromLong(0x1eadbeef);
82 #else
90 #else
83 val = PyInt_FromLong(0x1eadbeef);
91 val = PyInt_FromLong(0x1eadbeef);
84 #endif
92 #endif
85
93
86 if (val == NULL)
94 if (val == NULL)
87 goto bail;
95 goto bail;
88
96
89 PYLONG_VALUE(val) = 1;
97 PYLONG_VALUE(val) = 1;
90 ret = PyDict_SetItem(dirs, key, val);
98 ret = PyDict_SetItem(dirs, key, val);
91 Py_DECREF(val);
99 Py_DECREF(val);
92 if (ret == -1)
100 if (ret == -1)
93 goto bail;
101 goto bail;
94 Py_CLEAR(key);
102 Py_CLEAR(key);
95 }
103 }
96 ret = 0;
104 ret = 0;
97
105
98 bail:
106 bail:
99 Py_XDECREF(key);
107 Py_XDECREF(key);
100
108
101 return ret;
109 return ret;
102 }
110 }
103
111
104 static int _delpath(PyObject *dirs, PyObject *path)
112 static int _delpath(PyObject *dirs, PyObject *path)
105 {
113 {
106 char *cpath = PyBytes_AS_STRING(path);
114 char *cpath = PyBytes_AS_STRING(path);
107 Py_ssize_t pos = PyBytes_GET_SIZE(path);
115 Py_ssize_t pos = PyBytes_GET_SIZE(path);
108 PyObject *key = NULL;
116 PyObject *key = NULL;
109 int ret = -1;
117 int ret = -1;
110
118
111 while ((pos = _finddir(cpath, pos - 1)) != -1) {
119 while ((pos = _finddir(cpath, pos - 1)) != -1) {
112 PyObject *val;
120 PyObject *val;
113
121
114 key = PyBytes_FromStringAndSize(cpath, pos);
122 key = PyBytes_FromStringAndSize(cpath, pos);
115
123
116 if (key == NULL)
124 if (key == NULL)
117 goto bail;
125 goto bail;
118
126
119 val = PyDict_GetItem(dirs, key);
127 val = PyDict_GetItem(dirs, key);
120 if (val == NULL) {
128 if (val == NULL) {
121 PyErr_SetString(PyExc_ValueError,
129 PyErr_SetString(PyExc_ValueError,
122 "expected a value, found none");
130 "expected a value, found none");
123 goto bail;
131 goto bail;
124 }
132 }
125
133
126 if (--PYLONG_VALUE(val) <= 0) {
134 if (--PYLONG_VALUE(val) <= 0) {
127 if (PyDict_DelItem(dirs, key) == -1)
135 if (PyDict_DelItem(dirs, key) == -1)
128 goto bail;
136 goto bail;
129 } else
137 } else
130 break;
138 break;
131 Py_CLEAR(key);
139 Py_CLEAR(key);
132 }
140 }
133 ret = 0;
141 ret = 0;
134
142
135 bail:
143 bail:
136 Py_XDECREF(key);
144 Py_XDECREF(key);
137
145
138 return ret;
146 return ret;
139 }
147 }
140
148
141 static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
149 static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
142 {
150 {
143 PyObject *key, *value;
151 PyObject *key, *value;
144 Py_ssize_t pos = 0;
152 Py_ssize_t pos = 0;
145
153
146 while (PyDict_Next(source, &pos, &key, &value)) {
154 while (PyDict_Next(source, &pos, &key, &value)) {
147 if (!PyBytes_Check(key)) {
155 if (!PyBytes_Check(key)) {
148 PyErr_SetString(PyExc_TypeError, "expected string key");
156 PyErr_SetString(PyExc_TypeError, "expected string key");
149 return -1;
157 return -1;
150 }
158 }
151 if (skipchar) {
159 if (skipchar) {
152 if (!dirstate_tuple_check(value)) {
160 if (!dirstate_tuple_check(value)) {
153 PyErr_SetString(PyExc_TypeError,
161 PyErr_SetString(PyExc_TypeError,
154 "expected a dirstate tuple");
162 "expected a dirstate tuple");
155 return -1;
163 return -1;
156 }
164 }
157 if (((dirstateTupleObject *)value)->state == skipchar)
165 if (((dirstateTupleObject *)value)->state == skipchar)
158 continue;
166 continue;
159 }
167 }
160
168
161 if (_addpath(dirs, key) == -1)
169 if (_addpath(dirs, key) == -1)
162 return -1;
170 return -1;
163 }
171 }
164
172
165 return 0;
173 return 0;
166 }
174 }
167
175
168 static int dirs_fromiter(PyObject *dirs, PyObject *source)
176 static int dirs_fromiter(PyObject *dirs, PyObject *source)
169 {
177 {
170 PyObject *iter, *item = NULL;
178 PyObject *iter, *item = NULL;
171 int ret;
179 int ret;
172
180
173 iter = PyObject_GetIter(source);
181 iter = PyObject_GetIter(source);
174 if (iter == NULL)
182 if (iter == NULL)
175 return -1;
183 return -1;
176
184
177 while ((item = PyIter_Next(iter)) != NULL) {
185 while ((item = PyIter_Next(iter)) != NULL) {
178 if (!PyBytes_Check(item)) {
186 if (!PyBytes_Check(item)) {
179 PyErr_SetString(PyExc_TypeError, "expected string");
187 PyErr_SetString(PyExc_TypeError, "expected string");
180 break;
188 break;
181 }
189 }
182
190
183 if (_addpath(dirs, item) == -1)
191 if (_addpath(dirs, item) == -1)
184 break;
192 break;
185 Py_CLEAR(item);
193 Py_CLEAR(item);
186 }
194 }
187
195
188 ret = PyErr_Occurred() ? -1 : 0;
196 ret = PyErr_Occurred() ? -1 : 0;
189 Py_DECREF(iter);
197 Py_DECREF(iter);
190 Py_XDECREF(item);
198 Py_XDECREF(item);
191 return ret;
199 return ret;
192 }
200 }
193
201
194 /*
202 /*
195 * Calculate a refcounted set of directory names for the files in a
203 * Calculate a refcounted set of directory names for the files in a
196 * dirstate.
204 * dirstate.
197 */
205 */
198 static int dirs_init(dirsObject *self, PyObject *args)
206 static int dirs_init(dirsObject *self, PyObject *args)
199 {
207 {
200 PyObject *dirs = NULL, *source = NULL;
208 PyObject *dirs = NULL, *source = NULL;
201 char skipchar = 0;
209 char skipchar = 0;
202 int ret = -1;
210 int ret = -1;
203
211
204 self->dict = NULL;
212 self->dict = NULL;
205
213
206 if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
214 if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
207 return -1;
215 return -1;
208
216
209 dirs = PyDict_New();
217 dirs = PyDict_New();
210
218
211 if (dirs == NULL)
219 if (dirs == NULL)
212 return -1;
220 return -1;
213
221
214 if (source == NULL)
222 if (source == NULL)
215 ret = 0;
223 ret = 0;
216 else if (PyDict_Check(source))
224 else if (PyDict_Check(source))
217 ret = dirs_fromdict(dirs, source, skipchar);
225 ret = dirs_fromdict(dirs, source, skipchar);
218 else if (skipchar)
226 else if (skipchar)
219 PyErr_SetString(PyExc_ValueError,
227 PyErr_SetString(PyExc_ValueError,
220 "skip character is only supported "
228 "skip character is only supported "
221 "with a dict source");
229 "with a dict source");
222 else
230 else
223 ret = dirs_fromiter(dirs, source);
231 ret = dirs_fromiter(dirs, source);
224
232
225 if (ret == -1)
233 if (ret == -1)
226 Py_XDECREF(dirs);
234 Py_XDECREF(dirs);
227 else
235 else
228 self->dict = dirs;
236 self->dict = dirs;
229
237
230 return ret;
238 return ret;
231 }
239 }
232
240
233 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
241 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
234 {
242 {
235 PyObject *path;
243 PyObject *path;
236
244
237 if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
245 if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
238 return NULL;
246 return NULL;
239
247
240 if (_addpath(self->dict, path) == -1)
248 if (_addpath(self->dict, path) == -1)
241 return NULL;
249 return NULL;
242
250
243 Py_RETURN_NONE;
251 Py_RETURN_NONE;
244 }
252 }
245
253
246 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
254 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
247 {
255 {
248 PyObject *path;
256 PyObject *path;
249
257
250 if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
258 if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
251 return NULL;
259 return NULL;
252
260
253 if (_delpath(self->dict, path) == -1)
261 if (_delpath(self->dict, path) == -1)
254 return NULL;
262 return NULL;
255
263
256 Py_RETURN_NONE;
264 Py_RETURN_NONE;
257 }
265 }
258
266
259 static int dirs_contains(dirsObject *self, PyObject *value)
267 static int dirs_contains(dirsObject *self, PyObject *value)
260 {
268 {
261 return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
269 return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
262 }
270 }
263
271
264 static void dirs_dealloc(dirsObject *self)
272 static void dirs_dealloc(dirsObject *self)
265 {
273 {
266 Py_XDECREF(self->dict);
274 Py_XDECREF(self->dict);
267 PyObject_Del(self);
275 PyObject_Del(self);
268 }
276 }
269
277
270 static PyObject *dirs_iter(dirsObject *self)
278 static PyObject *dirs_iter(dirsObject *self)
271 {
279 {
272 return PyObject_GetIter(self->dict);
280 return PyObject_GetIter(self->dict);
273 }
281 }
274
282
275 static PySequenceMethods dirs_sequence_methods;
283 static PySequenceMethods dirs_sequence_methods;
276
284
277 static PyMethodDef dirs_methods[] = {
285 static PyMethodDef dirs_methods[] = {
278 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
286 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
279 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
287 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
280 {NULL} /* Sentinel */
288 {NULL} /* Sentinel */
281 };
289 };
282
290
283 static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) };
291 static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) };
284
292
285 void dirs_module_init(PyObject *mod)
293 void dirs_module_init(PyObject *mod)
286 {
294 {
287 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
295 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
288 dirsType.tp_name = "parsers.dirs";
296 dirsType.tp_name = "parsers.dirs";
289 dirsType.tp_new = PyType_GenericNew;
297 dirsType.tp_new = PyType_GenericNew;
290 dirsType.tp_basicsize = sizeof(dirsObject);
298 dirsType.tp_basicsize = sizeof(dirsObject);
291 dirsType.tp_dealloc = (destructor)dirs_dealloc;
299 dirsType.tp_dealloc = (destructor)dirs_dealloc;
292 dirsType.tp_as_sequence = &dirs_sequence_methods;
300 dirsType.tp_as_sequence = &dirs_sequence_methods;
293 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
301 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
294 dirsType.tp_doc = "dirs";
302 dirsType.tp_doc = "dirs";
295 dirsType.tp_iter = (getiterfunc)dirs_iter;
303 dirsType.tp_iter = (getiterfunc)dirs_iter;
296 dirsType.tp_methods = dirs_methods;
304 dirsType.tp_methods = dirs_methods;
297 dirsType.tp_init = (initproc)dirs_init;
305 dirsType.tp_init = (initproc)dirs_init;
298
306
299 if (PyType_Ready(&dirsType) < 0)
307 if (PyType_Ready(&dirsType) < 0)
300 return;
308 return;
301 Py_INCREF(&dirsType);
309 Py_INCREF(&dirsType);
302
310
303 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
311 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
304 }
312 }
General Comments 0
You need to be logged in to leave comments. Login now