Why is string's startswith slower than in?
令人惊讶的是,我发现
1 2 3 4 5 6 7 | In [10]: s="ABCD"*10 In [11]: %timeit s.startswith("XYZ") 1000000 loops, best of 3: 307 ns per loop In [12]: %timeit"XYZ" in s 10000000 loops, best of 3: 81.7 ns per loop |
众所周知,
当
1 2 3 4 5 6 7 | In [13]: s="ABCD"*200 In [14]: %timeit s.startswith("XYZ") 1000000 loops, best of 3: 306 ns per loop In [15]: %timeit"XYZ" in s 1000000 loops, best of 3: 666 ns per loop |
号
因此,调用
然后我试图弄清楚
首先,我使用了一个
1 2 3 4 | In [16]: f=s.startswith In [17]: %timeit f("XYZ") 1000000 loops, best of 3: 270 ns per loop |
此外,我还测试了空函数调用的成本:
1 2 3 4 | In [18]: def func(a): pass In [19]: %timeit func("XYZ") 10000000 loops, best of 3: 106 ns per loop |
。
不考虑点操作和函数调用的开销,
根据poke和lvc的建议,在
1 2 3 4 5 | In [28]: %timeit s.startswith("XYZ") 1000000 loops, best of 3: 314 ns per loop In [29]: %timeit s.__contains__("XYZ") 1000000 loops, best of 3: 192 ns per loop |
如注释中所述,如果使用
您可以通过查看字节码来了解这一点:
1 2 3 4 5 6 7 8 9 10 11 | >>> dis.dis('"XYZ" in s') 1 0 LOAD_CONST 0 ('XYZ') 3 LOAD_NAME 0 (s) 6 COMPARE_OP 6 (in) 9 RETURN_VALUE >>> dis.dis('s.__contains__("XYZ")') 1 0 LOAD_NAME 0 (s) 3 LOAD_ATTR 1 (__contains__) 6 LOAD_CONST 0 ('XYZ') 9 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 12 RETURN_VALUE |
因此,将
为了实现这一点,您可以检查这两者的实现。对于包含实现,有趣的是它是静态类型的,并且只假设参数是Unicode对象本身。所以这是相当有效的。
但是,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | static PyObject * unicode_startswith(PyObject *self, PyObject *args) { // argument parsing PyObject *subobj; PyObject *substring; Py_ssize_t start = 0; Py_ssize_t end = PY_SSIZE_T_MAX; int result; if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end)) return NULL; // tuple handling if (PyTuple_Check(subobj)) {} // unicode conversion substring = PyUnicode_FromObject(subobj); if (substring == NULL) {} // actual implementation result = tailmatch(self, substring, start, end, -1); Py_DECREF(substring); if (result == -1) return NULL; return PyBool_FromLong(result); } |
号
这可能是
这可能是因为