为什么比较匹配的字符串比不匹配的字符串更快



这里有两个测量值:

timeit.timeit('"toto"=="1234"', number=100000000)
1.8320042459999968
timeit.timeit('"toto"=="toto"', number=100000000)
1.4517491540000265

正如您所看到的,比较两个匹配的字符串比比较两个大小相同但不匹配的字符串更快。这相当令人不安:在字符串比较过程中,我认为Python正在逐个字符地测试字符串,因此"toto"=="toto"的测试时间应该比"toto"=="1234"长,因为它需要四个测试,而一个测试用于不匹配的比较。也许比较是基于哈希的,但在这种情况下,两种比较的时间应该相同。

为什么?

结合我的评论和@khelwood的评论:

TL;DR:
分析两个比较的字节码时,会发现'time''time'字符串被分配给同一对象。因此,前置身份检查(在C级(是提高比较速度的原因。

相同对象分配的原因是,作为实现细节,CPython实习生字符串只包含"名称字符"(即字母和下划线字符(。这将启用对象的标识检查。


字节码:

import dis
In [24]: dis.dis("'time'=='time'")
1           0 LOAD_CONST               0 ('time')  # <-- same object (0)
2 LOAD_CONST               0 ('time')  # <-- same object (0)
4 COMPARE_OP               2 (==)
6 RETURN_VALUE
In [25]: dis.dis("'time'=='1234'")
1           0 LOAD_CONST               0 ('time')  # <-- different object (0)
2 LOAD_CONST               1 ('1234')  # <-- different object (1)
4 COMPARE_OP               2 (==)
6 RETURN_VALUE

分配时间:

在时间测试中使用赋值也可以看到"加速"。两个变量对同一字符串的赋值(和比较(比两个变量向不同字符串的赋值更快。进一步支持该假设的基本逻辑是执行对象比较。这将在下一节中得到确认。

In [26]: timeit.timeit("x='time'; y='time'; x==y", number=1000000)
Out[26]: 0.0745926329982467
In [27]: timeit.timeit("x='time'; y='1234'; x==y", number=1000000)
Out[27]: 0.10328884399496019

Python源代码:

正如@mkrieger1和@Masklinn在他们的注释中提供的那样,unicodeobject.c的源代码首先执行指针比较,如果是True,则立即返回。

int
_PyUnicode_Equal(PyObject *str1, PyObject *str2)
{
assert(PyUnicode_CheckExact(str1));
assert(PyUnicode_CheckExact(str2));
if (str1 == str2) {                  // <-- Here
return 1;
}
if (PyUnicode_READY(str1) || PyUnicode_READY(str2)) {
return -1;
}
return unicode_compare_eq(str1, str2);
}

附录:

  • 参考答案很好地说明了如何读取反汇编的字节码输出。由@Delgan提供
  • 参考答案,很好地描述了CPython的字符串实习。@ShadowRanger的堂屋

比较匹配的字符串并不总是快。相反,比较共享相同id的字符串总是更快。身份确实是这种行为的原因(正如@S3DEV精彩地解释的那样(的证据是:

>>> x = 'toto'
>>> y = 'toto'
>>> z = 'totoo'[:-1]
>>> w = 'abcd'
>>> x == y
True
>>> x == z
True
>>> x == w
False
>>> id(x) == id(y)
True
>>> id(x) == id(z)
False
>>> id(x) == id(w)
False
>>> timeit.timeit('x==y', number=100000000, globals={'x': x, 'y': y})
3.893762200000083
>>> timeit.timeit('x==z', number=100000000, globals={'x': x, 'z': z})
4.205321462000029
>>> timeit.timeit('x==w', number=100000000, globals={'x': x, 'w': w})
4.15288594499998

比较具有相同id的对象总是更快(正如您从示例中注意到的,xz之间的比较比xy之间的比较慢,这是因为xz不共享相同的id(。

相关内容

  • 没有找到相关文章

最新更新