为什么执行startwith
比切片慢?
In [1]: x = 'foobar'
In [2]: y = 'foo'
In [3]: %timeit x.startswith(y)
1000000 loops, best of 3: 321 ns per loop
In [4]: %timeit x[:3] == y
10000000 loops, best of 3: 164 ns per loop
出人意料的是,甚至包括计算长度,切片仍然出现显著快:
In [5]: %timeit x[:len(y)] == y
1000000 loops, best of 3: 251 ns per loop
注:此行为的第一部分中指出的Python数据分析 (第3章),但它没有解释提供。
。
如果有帮助的: 这里是C代码startswith
; 这里是输出dis.dis
:
In [6]: import dis
In [7]: dis_it = lambda x: dis.dis(compile(x, '<none>', 'eval'))
In [8]: dis_it('x[:3]==y')
1 0 LOAD_NAME 0 (x)
3 LOAD_CONST 0 (3)
6 SLICE+2
7 LOAD_NAME 1 (y)
10 COMPARE_OP 2 (==)
13 RETURN_VALUE
In [9]: dis_it('x.startswith(y)')
1 0 LOAD_NAME 0 (x)
3 LOAD_ATTR 1 (startswith)
6 LOAD_NAME 2 (y)
9 CALL_FUNCTION 1
12 RETURN_VALUE
有些性能差异可以通过考虑所花费的时间来解释.
运营商做它的事:
>>> x = 'foobar'
>>> y = 'foo'
>>> sw = x.startswith
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 316 ns per loop
>>> %timeit sw(y)
1000000 loops, best of 3: 267 ns per loop
>>> %timeit x[:3] == y
10000000 loops, best of 3: 151 ns per loop
差的另一部分可以通过以下事实来解释startswith
是一个函数 ,和甚至无操作函数调用需要一点时间:
>>> def f():
... pass
...
>>> %timeit f()
10000000 loops, best of 3: 105 ns per loop
使用切片这不能完全解释这一差别,因为该版本和len
调用一个函数,并仍然较快(与sw(y)
以上- 267纳秒):
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 213 ns per loop
我在这里只猜测是,也许Python的优化查找时间为内置函数,或len
呼叫高度优化的(这可能是真的)。 这可能是可以测试与自定义len
FUNC。 或者,也许这就是查明的差异LastCoder踢。还要注意larsmans '的结果,这表明startswith
实际上更快更长的字符串。 上述推理的整条生产线只适用于在那里我实际上是在谈论开销事项的情况下。
比较是不公平的,因为你只测量其中的情况下startswith
返回True
。
>>> x = 'foobar'
>>> y = 'fool'
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 221 ns per loop
>>> %timeit x[:3] == y # note: length mismatch
10000000 loops, best of 3: 122 ns per loop
>>> %timeit x[:4] == y
10000000 loops, best of 3: 158 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 210 ns per loop
>>> sw = x.startswith
>>> %timeit sw(y)
10000000 loops, best of 3: 176 ns per loop
此外,对于更长的字符串, startswith
是快了很多:
>>> import random
>>> import string
>>> x = '%030x' % random.randrange(256**10000)
>>> len(x)
20000
>>> y = r[:4000]
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 211 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 469 ns per loop
>>> sw = x.startswith
>>> %timeit sw(y)
10000000 loops, best of 3: 168 ns per loop
这仍然是正确的,当没有比赛。
# change last character of y
>>> y = y[:-1] + chr((ord(y[-1]) + 1) % 256)
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 210 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 470 ns per loop
>>> %timeit sw(y)
10000000 loops, best of 3: 168 ns per loop
# change first character of y
>>> y = chr((ord(y[0]) + 1) % 256) + y[1:]
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 210 ns per loop
>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 442 ns per loop
>>> %timeit sw(y)
10000000 loops, best of 3: 168 ns per loop
所以, startswith
可能是短字符串,因为它是长句子优化慢。
(绝招摆脱采取随机字符串这个答案 。)
startswith
比切片更复杂...
2924 result = _string_tailmatch(self,
2925 PyTuple_GET_ITEM(subobj, i),
2926 start, end, -1);
这不是一个简单的人物比较环路在草垛发生的事情的开端针。 我们正在寻找一个for循环,通过矢量/元组(subobj)迭代并调用另一个函数( _string_tailmatch
就可以了)。 多个函数调用的开销都与问候到堆栈,参数完整性检查等..
startswith
是一个库函数,而切片似乎是语言内置的。
2919 if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
2920 return NULL;
引述的文档 , startswith
也越多,你可能会想:
str.startswith(prefix[, start[, end]])
返回True
如果字符串以前缀开头,否则返回False
。 前缀也可以前缀查找的元组。 通过可选的启动 ,测试字符串开头的那个位置。 通过可选的结束 ,停在那个位置比较字符串。
调用一个函数是相当昂贵的。 我不知道,但是,如果是这种情况,以及对C写的内置函数
要知道,虽然,切片可能涉及的函数调用,以及根据所使用的对象。