Python3性能优化提示

2024-10-01 00:29:14 发布

您现在位置:Python中文网/ 问答频道 /正文

pep484说“将类型提示用于性能优化是留给读者的练习。”这对我来说,就像普通的Lisp一样,当我发誓我知道自己在做什么时,类型声明可以用来在性能密集型函数中搁置类型分派。为了亲自尝试,我创建了一个小基准,用p系列计算pi。首先我用天真的方式来做,然后我试着变得聪明,利用类型提示来提高性能:

import math
import time

def baselpi0(n):
    baselsum = 0;
    for i in range(1,n):
        baselsum += 1.0 / (i * i)
    return math.sqrt(6.0 * baselsum)

def baselpi1(n : int) -> float:
    n = float(n)
    baselsum  = 0.0
    i = 1.0
    while i < n:
        baselsum += 1.0 / (i * i)
        i += 1.0
    return math.sqrt(6.0 * baselsum)

start = time.time()
print(baselpi0(1000000000))
end = time.time()
print(end - start)
start = time.time()
print(baselpi1(1000000000))
end = time.time()
print(end - start)

我试图模仿的常见Lisp类比是:

^{pr2}$

在我的机器上,使用sbcl运行的lisp版本对于慢版本需要22秒,对于类型提示版本需要4秒,与C.CPython对于天真版本需要162秒,对于类型提示版本需要141秒。Pypy只需不到5秒钟就可以运行非类型提示版本,但是库支持对于我的项目来说还不够好。在

有没有一种方法可以改进我的类型提示版本,使性能更接近lisp或Pypy?在


Tags: import版本类型returntimedefmathsqrt
2条回答

速度差不是由类型暗示引起的。Python目前以及在可预见的将来,just会丢弃您提供的任何提示,并继续像以前一样动态执行。在

这是因为在一种情况下,您在整个代码中使用浮动算术(这会导致更快的执行速度),而在另一种情况下则不使用

例如:将baselpi1更改为以下内容:

def baselpi1(n : int) -> float:
    n = float(n)
    baselsum  = 0
    i = 1
    while i < n:
        baselsum += 1.0 / (i * i)
        i += 1
    return math.sqrt(6.0 * baselsum)

看看现在的执行时间:

^{pr2}$

是的,速度慢得多。在

如果您需要进行大量的数值计算,那么^{}通常是一个不错的选择。Numpy可以处理较低级别的数据类型(例如固定宽度的整数python的是无界的)。这会给你一种暗示你感兴趣的类型。由于numpy设计用于处理具有已知类型的数组中的大量数据,因此它可以在整个数组上高效地执行相同的操作。这也允许numpy与那些SIMD指令的CPU很好地工作(我不知道没有SIMD的现代CPU)。在

我通常会这样重写您的函数:

import math
import numpy

def baselpi_numpy(n):
    i = numpy.arange(1, n) # array of 1..n
    baselsum = (1 / (i * i)).sum()
    return math.sqrt(6 * baselsum)

但是,对于大的n,您将没有足够的内存。您需要添加一些额外的代码来批处理操作。即:

^{pr2}$

我5.2秒后在我的笔记本上得到结果。虽然我没有测试您使用的n的值,但是对于较低的n来说,numpy版本的速度要快20倍以上。在

相关问题 更多 >