计算能力的速度(在python中)

时间:2009-06-19 19:46:44

标签: python algorithm performance

我很好奇为什么它的繁殖速度比在python中强大得多(尽管从我读过的内容来看,这在很多其他语言中也是如此)。例如,它做得快得多

x*x

大于

x**2

我认为**运算符更通用,也可以处理分数幂。但是,如果这就是为什么它如此慢,为什么它不执行int指数检查然后只是进行乘法?

编辑:以下是我尝试的一些示例代码...

def pow1(r, n):
  for i in range(r):
    p = i**n

def pow2(r, n):
  for i in range(r):
    p = 1
    for j in range(n):
      p *= i

现在,pow2只是一个简单的例子,显然没有优化! 但即便如此,我发现使用n = 2且r = 1,000,000,则pow1需要~2500ms而pow2需要~1700ms。  我承认,对于大的n值,pow1确实比pow2快得多。但这并不太令人惊讶。

6 个答案:

答案 0 :(得分:23)

基本上天真的乘法是O(n),具有非常低的常数因子。取幂是O(log n),具有更高的常数因子(有特殊情况需要测试......分数指数,负指数等)。编辑:要清楚,那就是O(n),其中n是指数。

当然,对于小n来说,天真的方法会更快,你只是真正实现了一小部分指数数学,所以你的常数因子可以忽略不计。

答案 1 :(得分:6)

添加支票也是一项费用。你总是想在那里检查吗?编译语言可以检查一个常量指数,看看它是否是一个相对较小的整数,因为没有运行时成本,只是一个编译时成本。解释性语言可能无法进行检查。

除非语言指定了这种细节,否则由特定的实现决定。

Python不知道你要为它提供什么样的指数分布。如果它将是99%的非整数值,您是否希望代码每次检查一个整数,使运行时更慢?

答案 2 :(得分:3)

在指数检查中执行此操作会减慢非常轻微的两次幂的情况,因此不一定是胜利。然而,在指数事先已知的情况下(例如,使用文字2),生成的字节码可以通过简单的窥视孔优化来优化。据推测,这根本不值得做(这是一个相当具体的案例)。

这是一个快速的概念证明,可以进行这样的优化(可用作装饰器)。注意:您需要byteplay模块来运行它。

import byteplay, timeit

def optimise(func):
    c = byteplay.Code.from_code(func.func_code)
    prev=None
    for i, (op, arg) in enumerate(c.code):
        if op == byteplay.BINARY_POWER:
            if c.code[i-1] == (byteplay.LOAD_CONST, 2):
                c.code[i-1] = (byteplay.DUP_TOP, None)
                c.code[i] = (byteplay.BINARY_MULTIPLY, None)
    func.func_code = c.to_code()
    return func

def square(x):
    return x**2

print "Unoptimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000)
square = optimise(square)
print "Optimised   :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000)

给出了时间:

Unoptimised : 6.42024898529
Optimised   : 4.52667593956

<强> [编辑] 实际上,考虑一下,有一个很好的理由说明为什么这种优化没有完成。无法保证有人不会创建一个用户定义的类来覆盖__mul____pow__方法,并为每个方法执行不同的操作。安全地执行此操作的唯一方法是,如果您可以保证堆栈顶部的对象是具有相同结果的对象“x **2”和“x*x”,那么解决这个问题的方法是更难。例如。在我的例子中,这是不可能的,因为任何对象都可以传递给square函数。

答案 3 :(得分:3)

带有二进制求幂的b ^ p的实现

def power(b, p):
    """
    Calculates b^p
    Complexity O(log p)
    b -> double
    p -> integer
    res -> double
    """
    res = 1

    while p:
        if p & 0x1: res *= b
        b *= b
        p >>= 1

    return res

答案 4 :(得分:1)

我怀疑没有人期待这一切都那么重要。通常情况下,如果你想进行严肃的计算,你可以用Fortran或C或C ++或类似的东西来做(也许可以用Python调用它们)。

将所有内容视为exp(n * log(x))在n不是整数或非常大的情况下效果很好,但对于小整数来说效率相对较低。检查n是否是足够小的整数确实需要时间,并且会增加复杂性。

检查是否值得,取决于预期的指数,在这里获得最佳性能的重要性,以及额外复杂性的成本。显然,Guido和其他Python团伙决定检查不值得。

如果您愿意,可以编写自己的重复乘法函数。

答案 5 :(得分:0)

x x x x x怎么样? 它仍然比x ** 5快吗?

当int exponents变大时,获取幂可能比乘法更快。 但实际交叉发生的数量取决于各种条件,因此在我看来,这就是为什么在语言/库级别没有完成(或无法完成)优化的原因。但是用户仍然可以针对某些特殊情况进行优化:)