使用指数运算0.5效率低于math.sqrt?
这段话摘自一本叫《Python编程:计算机科学导论》的书。
我们本可以用指数运算符 ** 来计算平方根,但使用 math.sqrt 更有效率一些。
这里说的“更有效率一些”,但具体有多有效率呢?是怎么回事呢?
5 个回答
1
我猜测,math.sqrt这个函数可能是用的牛顿法,这种方法收敛速度很快,能很快找到结果。而指数运算可能用的是其他方法,速度就比较慢。
11
不用猜测具体的实现,我们可以直接看代码!
math.sqrt
其实是对标准C库中的 sqrt
的一个简单封装:可以查看 mathmodule.c
的第956行。
**
这个运算符的实现方式会根据参数的类型不同而不同,但当指数是浮点数时,它最终会调用标准C库中的 pow
函数(可以查看 floatobject.c
的第783行)。
现代的CPU通常有专门的平方根指令,而一般的指数运算程序并不会使用这些指令(可以对比一下 pow
和 sqrt
在glibc中的实现,特别是针对x86-64的)。但是一旦加上了解释器的开销(比如字节码、类型检查、方法调用等),原始速度的差异就不那么重要了,可能还会被你是直接调用 sqrt
还是通过 math
模块查找来影响(这在其他答案中的计时结果中有体现)。
16
理论上,hammar的回答和duffymo的回答都是不错的猜测。但是在我自己的机器上,实际情况是并没有更高效:
>>> import timeit
>>> timeit.timeit(stmt='[n ** 0.5 for n in range(100)]', setup='import math', number=10000)
0.15518403053283691
>>> timeit.timeit(stmt='[math.sqrt(n) for n in range(100)]', setup='import math', number=10000)
0.17707490921020508
问题的一部分在于.
这个操作。如果你直接把sqrt
导入到命名空间中,你会得到一点小的提升。
>>> timeit.timeit(stmt='[sqrt(n) for n in range(100)]', setup='from math import sqrt', number=10000)
0.15312695503234863
这里的关键词是:一点点。
进一步的测试表明,随着数字变大,使用sqrt
的好处会增加。但增加的幅度仍然不大!
>>> timeit.timeit(stmt='[n ** 0.5 for n in range(1000000)]', setup='import math', number=1)
0.18888211250305176
>>> timeit.timeit(stmt='[math.sqrt(n) for n in range(1000000)]', setup='import math', number=1)
0.18425297737121582
>>> timeit.timeit(stmt='[sqrt(n) for n in range(1000000)]', setup='from math import sqrt', number=1)
0.1571958065032959