在python中查找序列长度的开销?

时间:2015-12-04 22:33:33

标签: python optimization

我将在 python3 中找到序列的长度,发现range现在是<class type>而不是<type builtin_function_or_method>

这样,range调用似乎在内存中创建了一个生成器而没有创建list的开销,并在 python2 中填充它(如果我,请纠正我这是误解,或者我错了。)

我现在的问题是,计算序列长度会有明显的改善吗,我称之为len(range(start, stop, step))而不是计算int(math.ceil((stop - start) / step))

这是我将要做的一个粗略的例子:

s = list(range(limit))
s[1] = 0
for i in range(2, sqrtn + 1):
    if s[i]:
        s[i*i: limit: i] = [0] * len(range(i*i, limit, i)) # call to range

上述实际上比计算长度更有效吗?

from math import ceil

s = list(range(limit))
s[1] = 0
for i in range(2, sqrtn + 1):
if s[i]:
    s[i*i: limit: i] = [0] * int(math.ceil((limit - i*i) / i)) # no range call

1 个答案:

答案 0 :(得分:1)

理论上,是的,在实践中,没有。 Windows x64构建CPython 3.5.0的所有时间(特定时间无关紧要;每种方法的相对时间都是重要的):

>>> from math import ceil
>>> start, stop, step = 100*100, 100000, 100
>>> min(timeit.repeat('(stop - start + (step - 1)) // step', 'from __main__ import start, stop, step', number=100000))
0.016031580173375914
>>> min(timeit.repeat('ceil((stop - start) / step)', 'from __main__ import start, stop, step, ceil', number=100000))
0.024184756985505373
>>> min(timeit.repeat('len(range(start, stop, step))', 'from __main__ import start, stop, step', number=100000))
0.03917228338013956

我用几个不同的端点运行这些测试;如果值足够大以至于无法在Py_ssize_t中完成数学运算,rangeceil方法会更加接近(ceil减慢速度),但纯int数学方法赢得了我所经历的每一项测试。 rangeceil都有问题;对于非常大的数字,range会抛出OverflowError(它不能包含比Py_ssize_t更多的元素)和ceil(或更确切地说,浮点数)当你超过~53位值时,ceil之前的除法将产生浮点精度误差。纯int数学既快又可靠,应该是首选。

也就是说,其他Python解释器(PyPy,IronPython,Jython,Cython)可以使用像range这样的特殊情况(以及整数数学),并且可以很容易地具有完全不同的性能特征。

这里的实际开销不是len计算。 range实际上计算长度并在构建期间将其缓存在内部;检索它与任何命名函数调用一样接近free(并且所有内置序列都是如此;最坏的情况是它们必须从C级int构造Python级int,但所有数学运算都做同样的事情)。检索长度的实际成本:

>>> min(timeit.repeat('len(r)', 'from __main__ import start, stop, step; r = range(start, stop, step)', number=100000))
0.0076398965929911355