正文
,
best
of
3
:
51.9
ns per
loop
静态类型代码现在花费的时间为 51.9 纳秒,比最初的基准测试快约 60,000(六万)倍。
如果我们想计算任意输入的 Fibonacci 数,我们应坚持使用无类型版本,该版本的运行速度快 3,500 倍。还不错,对吧?
使用 Numba 编译
让我们使用另一个名为 Numba 的工具。它是针对部分 Python 版本的一个即时
(jit) 编译器。它不是对所有 Python 版本都适用,但在适用的情况下,它会带来奇迹。
安装它可能很麻烦。推荐使用像 Anaconda 这样的 Python 发行版或一个已安装了 Numba 的 Docker 镜像。完成安装后,我们导入它的 jit 编译器:
from numba import jit
它的使用非常简单。我们仅需要向想要编译的函数添加一点修饰。我们的代码变成了:
@
jit
def fib_seq_numba
(
n
)
:
if
n
<
2
:
return
n
(
a
,
b
)
=
(
1
,
0
)
for
i
in
range
(
n
-
1
)
:
(
a
,
b
)
=
(
a
+
b
,
a
)
return
a
对它计时会得到:
1000000 loops, best of 3:225 ns per loop
比无类型的 Cython 代码更快,比最初的 Python 代码快约 16,000 倍!
使用 Numpy
我们现在来看看第二项基准测试。它是快速排序算法的实现。Julia 团队使用了以下 Python 代码:
def qsort_kernel
(
a
,
lo
,
hi
)
:
i
=
lo
j
=
hi
while
i
<
hi
:
pivot
=
a
[(
lo
+
hi
)
// 2]
while
i
<=
j
:
while
a
[
i
]
<
pivot
:
i
+=
1
while
a
[
j
]
>
pivot
:
j
-=
1
if
i
<=
j
:
a
[
i
],
a
[
j
]
=
a
[
j
],
a
[
i
]
i
+=
1
j
-=
1
if
lo
<
j
:
qsort_kernel
(
a
,
lo
,
j
)
lo
=
i
j
=
hi
return
a
我将他们的基准测试代码包装在一个函数中:
import random
def benchmark_qsort
()
:
lst
=
[
random
.
random
()
for
i
in
range
(
1
,
5000
)
]
qsort_kernel
(
lst
,
0
,
len
(
lst
)
-
1
)
对它计时会得到:
100 loops, best of 3:18.3 ms per loop
上述代码与 C 代码非常相似。Cython 应该能很好地处理它。除了使用 Cython 和静态类型之外,让我们使用 Numpy
数组代替列表。在数组大小较大时,比如数千个或更多元素,Numpy 数组确实比Python 列表更快。
安装 Numpy 可能会花一些时间,推荐使用 Anaconda 或一个已安装了 Python 科学工具组合的 Docker 镜像。
在使用 Cython 时,需要将 Numpy 导入到应用了 Cython 的单元中。在使用 C 类型时,还必须使用 cimport 将它作为 C 模块导入。Numpy数组使用一种表示数组元素类型和数组维数(一维、二维等)的特殊语法来声明。
%
%
cython
import numpy
as
np
cimport numpy
as
np
cpdef
np
.
ndarray
[
double
,
ndim
=
1
]
\
qsort_kernel_cython_numpy_type
(
np
.
ndarray
[
double
,
ndim
=
1
]
a
,
\
long
lo
,
\
long
hi
)
:
cdef
:
long
i
,
j
double
pivot
i
=
lo
j
=
hi
while
i
<
hi
:
pivot
=
a
[(
lo
+
hi
)
// 2]
while
i
<=
j
:
while
a
[
i
]
<
pivot
:
i
+=
1
while
a
[
j
]
>
pivot
:
j
-=
1
if
i
<=
j
:
a
[
i
],
a
[
j
]
=
a
[
j
],
a
[
i
]
i
+=
1