Py学习  »  Python

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

Eric • 3 年前 • 1156 次点击  

以下是两个测量值:

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

如您所见,比较两个匹配的字符串比比较两个大小相同但不匹配的字符串要快。 这相当令人不安:在字符串比较期间,我认为Python正在逐个字符测试字符串,所以 "toto"=="toto" 测试的时间应该比 "toto"=="1234" 因为它需要四次测试,而非匹配比较则需要一次测试。也许比较是基于散列的,但在这种情况下,两种比较的计时应该相同。

为什么?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/132159
 
1156 次点击  
文章 [ 2 ]  |  最新文章 3 年前
Riccardo Bucco
Reply   •   1 楼
Riccardo Bucco    3 年前

不是 总是 比较匹配字符串的速度更快。相反,比较共享相同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的对象总是更快(正如您从示例中注意到的,比较 x z 相比于 十、 y ,那是因为 十、 Z 不要共享相同的id)。

S3DEV
Reply   •   2 楼
S3DEV    3 年前

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

TL;博士:
在分析字节码进行这两种比较时,它揭示了 'time' “时间” 字符串被指定给同一个对象。因此 身份检查 (C级)是比较速度提高的原因。

相同对象赋值的原因是 实施细节 ,CPython实习生字符串,仅包含“名称字符”(即alpha和下划线字符)。这将启用对象的身份检查。


字节码:

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和@masklin在他们的评论中所提供的帮助 source code 对于 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);
}

附录:

  • Reference answer 很好地演示了如何读取反汇编字节码输出。由@Delgan提供
  • Reference answer 这很好地描述了CPython的弦乐训练。《暗影游侠》中的Coutresy