comparison mercurial/parsers.c @ 22778:80f2b63dd83a

parsers: add a function to efficiently lowercase ASCII strings We need a way to efficiently lowercase ASCII strings. For example, 'hg status' needs to build up the fold map -- a map from a canonical case (for OS X, lowercase) to the actual case of each file and directory in the dirstate. The current way we do that is to try decoding to ASCII and then calling lower() on the string, labeled 'orig' below: str.decode('ascii') return str.lower() This is pretty inefficient, and it turns out we can do much better. I also tested out a condition-based approach, labeled 'cond' below: (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c 'cond' turned out to be slower in all cases. A 256-byte lookup table with invalid values for everything past 127 performed similarly, but this was less verbose. On OS X 10.9 with LLVM version 6.0 (clang-600.0.51), the asciilower function was run against two corpuses. Corpus 1 (list of files from real-world repo, > 100k files): orig: wall 0.428567 comb 0.430000 user 0.430000 sys 0.000000 (best of 24) cond: wall 0.077204 comb 0.070000 user 0.070000 sys 0.000000 (best of 100) lookup: wall 0.060714 comb 0.060000 user 0.060000 sys 0.000000 (best of 100) Corpus 2 (mozilla-central, 113k files): orig: wall 0.238406 comb 0.240000 user 0.240000 sys 0.000000 (best of 42) cond: wall 0.040779 comb 0.040000 user 0.040000 sys 0.000000 (best of 100) lookup: wall 0.037623 comb 0.040000 user 0.040000 sys 0.000000 (best of 100) On a Linux server-class machine with GCC 4.4.6 20120305 (Red Hat 4.4.6-4): Corpus 1 (real-world repo, > 100k files): orig: wall 0.260899 comb 0.260000 user 0.260000 sys 0.000000 (best of 38) cond: wall 0.054818 comb 0.060000 user 0.060000 sys 0.000000 (best of 100) lookup: wall 0.048489 comb 0.050000 user 0.050000 sys 0.000000 (best of 100) Corpus 2 (mozilla-central, 113k files): orig: wall 0.153082 comb 0.150000 user 0.150000 sys 0.000000 (best of 65) cond: wall 0.031007 comb 0.040000 user 0.040000 sys 0.000000 (best of 100) lookup: wall 0.028793 comb 0.030000 user 0.030000 sys 0.000000 (best of 100) SSE instructions might help even more, but I didn't experiment with those.
author Siddharth Agarwal <sid0@fb.com>
date Fri, 03 Oct 2014 18:42:39 -0700
parents 5e0d1478db8e
children 5715c93cb854
comparison
equal deleted inserted replaced
22777:bbb2f8b0459e 22778:80f2b63dd83a
33 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 35 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
36 }; 36 };
37 37
38 static char lowertable[128] = {
39 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
40 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
41 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
42 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
43 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
44 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
45 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
46 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
47 '\x40',
48 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
49 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
50 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
51 '\x78', '\x79', '\x7a', /* X-Z */
52 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
53 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
54 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
55 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
56 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
57 };
58
38 static inline int hexdigit(const char *p, Py_ssize_t off) 59 static inline int hexdigit(const char *p, Py_ssize_t off)
39 { 60 {
40 int8_t val = hextable[(unsigned char)p[off]]; 61 int8_t val = hextable[(unsigned char)p[off]];
41 62
42 if (val >= 0) { 63 if (val >= 0) {
68 int lo = hexdigit(str, i++); 89 int lo = hexdigit(str, i++);
69 *d++ = (hi << 4) | lo; 90 *d++ = (hi << 4) | lo;
70 } 91 }
71 92
72 return ret; 93 return ret;
94 }
95
96 static PyObject *asciilower(PyObject *self, PyObject *args)
97 {
98 char *str, *newstr;
99 int i, len;
100 PyObject *newobj = NULL;
101
102 if (!PyArg_ParseTuple(args, "s#", &str, &len))
103 goto quit;
104
105 newobj = PyBytes_FromStringAndSize(NULL, len);
106 if (!newobj)
107 goto quit;
108
109 newstr = PyBytes_AS_STRING(newobj);
110
111 for (i = 0; i < len; i++) {
112 char c = str[i];
113 if (c & 0x80) {
114 PyObject *err = PyUnicodeDecodeError_Create(
115 "ascii", str, len, i, (i + 1),
116 "unexpected code byte");
117 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
118 goto quit;
119 }
120 newstr[i] = lowertable[(unsigned char)c];
121 }
122
123 return newobj;
124 quit:
125 Py_XDECREF(newobj);
126 return NULL;
73 } 127 }
74 128
75 /* 129 /*
76 * This code assumes that a manifest is stitched together with newline 130 * This code assumes that a manifest is stitched together with newline
77 * ('\n') characters. 131 * ('\n') characters.
2163 static PyMethodDef methods[] = { 2217 static PyMethodDef methods[] = {
2164 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"}, 2218 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
2165 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"}, 2219 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
2166 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"}, 2220 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
2167 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"}, 2221 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
2222 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
2168 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"}, 2223 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
2169 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"}, 2224 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
2170 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"}, 2225 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
2171 {NULL, NULL} 2226 {NULL, NULL}
2172 }; 2227 };