分析Cython代码,我们将分析用来计算欧拉常数近似值的Cython代码和NumPy代码。所需的计算公式请见http://en.wikipedia.org/wiki/E_%28mathematical_constant%29。
具体步骤
下面介绍怎样分析Cython代码。为此,请执行以下步骤。
- 用NumPy近似计算e
用NumPy近似计算e时,需要执行下列步骤。
- 创建一个由从1到n-1的自然数构成的数组(本例中n的默认值是40)。
- 计算数组的累积连乘,等同于求阶乘。
- 求取阶乘的倒数。
- 应用上述维基百科页面中的公式。
最后要加上标准的性能分析代码。具体代码如下:
这段代码在Windows平台上运行报错,原因是,默认情况下数组arr的数据类型是int32,不能容纳太大的计算结果。因此建议创建数组时,使用语句
arr = numpy.arange(1, n,dtype=numpy.uint64)
。
import numpy
import cProfile
import pstats
def approx_e(n=40):
# 创建数组[1, 2, ... n-1]
arr = numpy.arange(1, n)
# 计算阶乘,并把计算结果转换为浮点数。
arr = arr.cumprod().astype(float)
# 取倒数1/n
arr = numpy.reciprocal(arr)
print 1 + arr.sum()
cProfile.runctx("approx_e()", globals(), locals(), "Profile.prof")
s = pstats.Stats("Profile.prof")
s.strip_dirs().sort_stats("time").print_stats()
常数e近似计算的结果和性能分析的结果如下所示:
2.71828182846
7 function calls in 0.000 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 numpy_approxe.py:5(approx_e)
1 0.000 0.000 0.000 0.000 {method 'cumprod' of 'numpy.ndarray' objects}
1 0.000 0.000 0.000 0.000 {numpy.core.multiarray.arange}
1 0.000 0.000 0.000 0.000 {method 'sum' of 'numpy.ndarray' objects}
1 0.000 0.000 0.000 0.000 {method 'astype' of 'numpy.ndarray' objects}
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
- 用Cython近似计算e
和上述用NumPy近似计算e的过程一样,Cython代码使用相同的算法,只是在具体实现上有一些差别。和NumPy相比,由于缺少合适的便捷函数,我们需要用到for
循环。而且,我们需要指明某些变量的类型。.pyx文件的具体代码如下。
def approx_e(int n=40):
cdef double sum = 0.
cdef double factorial = 1.
cdef int k
for k in xrange(1,n+1):
factorial *= k
sum += 1/factorial
print 1 + sum
下列Python程序首先引入了Cython模块,然后做了一些相关的性能分析。
import pstats
import cProfile
import pyximport
pyximport.install()
import approxe
cProfile.runctx("approxe.approx_e()", globals(), locals(), "Profile.prof")
s = pstats.Stats("Profile.prof")
s.strip_dirs().sort_stats("time").print_stats()
Cython代码的分析结果如下:
2.71828182846
3 function calls in 0.000 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 {approxe.approx_e}
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
攻略小结
我们分别分析了NumPy和Cython代码。NumPy在执行速度方面已经做了深度优化,NumPy程序和Cython程序都表现出了良好的性能,对此我们不应觉得意外。