Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么创建从0到log(len(list),2)的范围这么慢?_Python_Performance_Sage - Fatal编程技术网

Python 为什么创建从0到log(len(list),2)的范围这么慢?

Python 为什么创建从0到log(len(list),2)的范围这么慢?,python,performance,sage,Python,Performance,Sage,我不知道为什么会这样。我正在处理一些列表,我需要一个从0到log(n,2)的循环,其中n是列表的长度。但是代码速度惊人地慢,所以经过一点研究,我发现问题出在范围生成上。演示的示例代码: n = len([1,2,3,4,5,6,7,8]) k = 8 timeit('range(log(n, 2))', number=2, repeat=3) # Test 1 timeit('range(log(k, 2))', number=2, repeat=3) # Test 2 输出 2 loops,

我不知道为什么会这样。我正在处理一些列表,我需要一个从0到
log(n,2)
循环,其中n是列表的长度。但是代码速度惊人地慢,所以经过一点研究,我发现问题出在范围生成上。演示的示例代码:

n = len([1,2,3,4,5,6,7,8])
k = 8
timeit('range(log(n, 2))', number=2, repeat=3) # Test 1
timeit('range(log(k, 2))', number=2, repeat=3) # Test 2
输出

2 loops, best of 3: 2.2 s per loop
2 loops, best of 3: 3.46 µs per loop
测试的数量很低(我不希望它运行超过10分钟),但它已经表明
范围(log(n,2))
比仅使用整数对数的对应项慢几个数量级。这真是令人惊讶,我不知道为什么会发生这种情况。可能是我电脑上的问题,可能是Sage问题或Python bug(我没有在Python上尝试过同样的问题)


使用
xrange
而不是
range
也没有帮助。此外,如果您使用
.n()
获取数字,测试1将以2的相同速度运行

有人知道会发生什么吗? 谢谢

也许首先使用
log(x,2)
(又称
ld()
)不是一个好主意。我建议使用移位int值来实现
ld()

这样,您就可以使用
range()
log()
来避免所有这些丑陋

在正常情况下,调用
log()

>>> timeit('for i in range(int(math.log(8, 2))): pass', setup='import math')
0.6762251853942871
>>> timeit('n = 8\nwhile n:\n  n >>= 1')
0.24107813835144043
n
的值越大,差异越小。对于
n=10000
我得到了0.8163230419158936和0.8106038570404053,但这应该是因为与循环初始化相比,循环体将占用大部分时间。

Python2允许范围(一些浮点),但它已弃用,在Python3中不起作用

代码示例未提供指定的输出。但我们可以走过去。首先,timeit需要一个完整的脚本,调用timeit的脚本中的导入没有使用:

>>> timeit('range(log(8,2))')
  Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.6/timeit.py", line 226, in timeit
    return Timer(stmt, setup, timer).timeit(number)
  File "/usr/lib64/python2.6/timeit.py", line 192, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
NameError: global name 'log' is not defined
如果将导入移动到设置中,效果会更好,但单次拍摄的计时非常不准确:

>>> timeit('range(log(8,2))',setup='from math import log')
1.9139349460601807
最后,运行几次,您会得到一个很好的数字:

>>> timeit('range(log(8,2))',setup='from math import log',number=100)
0.00038290023803710938

这看起来像是一只鼠尾草虫

我创建了一个新笔记本并完成了以下操作:

n = len([1,2,3,4,5,6,7,8])
k = 8
timeit('range(log(n, 2))', number=2, repeat=3) # Test 1
timeit('range(log(len([1,2,3,4,5,6,7,8]), 2))', number=2, repeat=3) # Test 1.5
timeit('range(log(k, 2))', number=2, repeat=3) # Test 2
测试1.5和测试1一样慢。但是,如果您以任何方式将其分解,请去掉
范围,甚至添加
m=n+0
,并使用
m
而不是
n
,它会下降到微秒

很明显,Sage试图在这里做一些复杂的事情,同时评估表达式,并感到困惑


要验证这一点,请在普通的旧ipython中:

n = len([1,2,3,4,5,6,7,8])
k = 8
%timeit 'range(log(n, 2))'
%timeit 'range(log(len([1,2,3,4,5,6,7,8]), 2))'
%timeit 'range(log(k, 2))'
正如你所料,它们都同样快


那…你怎么办

嗯,您可能想尝试追踪Sage bug并将其归档到上游。但与此同时,您可能希望在代码中找到一个变通方法

如上所述,只需执行
m=n+0
并使用
m
而不是
n
似乎可以加快速度。看看这对你有用吗?

天哪,我认识这个。它和我的一个朋友有关。首先,由于无聊的原因,使用Python
int
而不是Sage
Integer
会带来额外的开销:

sage: log(8, 2)
3
sage: type(log(8, 2))
sage.rings.integer.Integer
sage: log(8r, 2)
log(8)/log(2)
sage: type(log(8r, 2))
sage.symbolic.expression.Expression
sage: %timeit log(8, 2)
1000000 loops, best of 3: 1.4 us per loop
sage: %timeit log(8r, 2)
1000 loops, best of 3: 404 us per loop
(后缀
r
表示“原始”,并防止Sage编制者将文本
2
包装成
整数(2)

然后就变得很奇怪了。为了给
范围
产生一个int来消费,Sage必须弄清楚如何将
log(8)/log(2)
转换成3,结果是她做了最糟糕的事情。剽窃我的原始诊断(经过必要的修改):

首先,她检查这个对象是否有自己的获取int的方法,但它没有。所以她用log(8)/log(2)构建了一个RealInterval对象,结果证明这是她能做的最糟糕的事情了!她检查间隔的上下部分是否一致[在地板上,我的意思是](以便她确定地板是什么)。但在这种情况下,因为它实际上是一个整数这看起来总是像:

sage: y = log(8)/log(2)
sage: rif = RealIntervalField(53)(y)
sage: rif
3.000000000000000?
sage: rif.endpoints()
(2.99999999999999, 3.00000000000001)
这两个边界有不相等的楼层,因此Sage认为她还没有解决这个问题,她不断将精度提高到20000位,看看是否能证明它们是相等的。。但通过建造,它永远不会起作用。最后,她放弃了,并试图简化它,成功了:

sage: y.simplify_full()
3
无文字证明它是完全可分情形的反常性质:

sage: %timeit range(log(8r, 2))
1 loops, best of 3: 2.18 s per loop
sage: %timeit range(log(9r, 2))
1000 loops, best of 3: 766 us per loop
sage: %timeit range(log(15r, 2))
1000 loops, best of 3: 764 us per loop
sage: %timeit range(log(16r, 2))
1 loops, best of 3: 2.19 s per loop

听起来像是一个智者(也许是cython?)的问题。Python
range
甚至不接受浮动。Python在全局命名空间中也没有
log
(如果不向
timeit
添加
setup
,就无法实现它)。而且
n
timeit
也不可用。而且
timeit
上没有
repeat
参数(我假设您是从timeit import timeit
获得的
)。您的输出不是显示了
timeit
返回的值是随机的吗?毕竟你试了两次同样的方法(n和k都是8),得到的结果差别很大。你真的预先计算了n吗?“另外,如果你用
.n()
”得到数字。等等,什么?从哪里得到什么号码?好了,Sage是建立在ipython之上的,它所有的“神奇”语法都以
%
开头。我愿意承认,在Python中编写一个正确的位移位循环要比调用
log
慢得多。但无论我是对是错,你绝对不应该在不尝试测试的情况下断言它更快。也许你不应该假设我没有测试。在今天的处理器中,位移位并不比加法或其他简单的算术运算慢,这是我基本知识的一部分。但是我添加了一些测试来证明我的观点。好吧,你给苹果计时了
sage: y.simplify_full()
3
sage: %timeit range(log(8r, 2))
1 loops, best of 3: 2.18 s per loop
sage: %timeit range(log(9r, 2))
1000 loops, best of 3: 766 us per loop
sage: %timeit range(log(15r, 2))
1000 loops, best of 3: 764 us per loop
sage: %timeit range(log(16r, 2))
1 loops, best of 3: 2.19 s per loop