Python字符串比较性能不一致



我想检查字符串比较是如何工作的(我想看看它是否是一个字符一个字符,以及它是否在比较之前检查字符串的长度),所以我使用了以下代码:

s1 = 'abc'
s2 = 'abcd'
s3 = 'dbc'
s4 = 'abd'
t1 = time.clock()
s1==s2
print time.clock() - t1
t2 = time.clock()
s1==s3
print time.clock() - t2
t3 = time.clock()
s1==s4
print time.clock() - t3

当我在非常长的字符串(~30MB的文本文件)上尝试同样的事情时,它工作得很好,我发现它确实执行长度检查,并且它还比较一个字符一个字符。但是当我在短字符串(比如上面代码中的字符串)上尝试它时,性能结果非常不一致。有人知道为什么它们不一致或者我做错了什么吗?(也许我错了,这种比较并不像我想的那样有效?)

编辑:我也尝试过的一个例子是比较不同长度的字符串与特定的字符串。我认为执行时间最长的字符串将是另一个字符串的确切长度,因为其余部分将落在长度检查中,但它也不一致)。假设我要检查的字符串是'hello',所以我比较了'a', 'aa', 'aaa'等等…我原以为最长的支票会是'aaaaa',但它是'a',我不知道为什么。

字符串在比较内容之前比较长度是正确的(至少在2.7中)。以下是string_richcompare的相关部分:

if (op == Py_EQ) {
    /* Supporting Py_NE here as well does not save
       much time, since Py_NE is rarely used.  */
    if (Py_SIZE(a) == Py_SIZE(b)
        && (a->ob_sval[0] == b->ob_sval[0]
        && memcmp(a->ob_sval, b->ob_sval, Py_SIZE(a)) == 0)) {
        result = Py_True;
    } else {
        result = Py_False;
    }
    goto out;
}

简单来说,检查顺序如下:

  • 如果两个字符串有相同的内存地址,它们是相等的。(未在上述代码中显示)
  • 如果字符串的长度不同,则它们不相等。
  • 如果字符串的第一个字符不同,则它们不相等。
  • 如果字符串具有相同的字符数组,则它们相等。

第三次检查似乎不是严格必要的,但如果手动检查数组内容比调用memcmp更快,则可能是一种优化。


如果您的基准测试建议您比较不同长度的字符串比比较相同长度的字符串慢,这可能是由clock不完全可靠的行为引起的虚报,如其他答案和评论所述。

当测量非常小的时间时,您可能会得到不一致的结果。通过多次重复操作,您将获得更好的结果,因此差异很大:

t1 = time.clock()
for i in range(10**6):
    s1 == s2
t2 = time.clock()

更好的是,使用timeit模块来处理重复(和其他细节)例如为您关闭垃圾收集):

import timeit
s1 = 'abc'
s2 = 'abcd'
s3 = 'dbc'
s4 = 'abd'
t1 = timeit.timeit('s1==s2', 'from __main__ import s1, s2', number=10**8)
t2 = timeit.timeit('s1==s3', 'from __main__ import s1, s3', number=10**8)
t3 = timeit.timeit('s1==s4', 'from __main__ import s1, s4', number=10**8)
for t in (t1, t2, t3):
    print(t)
收益率

2.82305312157
2.83096408844
3.15551590919

因此s1==s2s1==s3花费的时间基本相同。s1==s4需要更多的时间,因为在相等返回False之前需要比较更多的字符。


顺便说一下,time.clocktimeit.default_timer用来测量在Windows上,time.timetimeit.default_timer用于测量在Unix上。使用timeit.default_timer代替time.clocktime.time

最新更新