向量运算入门
本节首先介绍向量操作的基本概念,这些概念对于理解高性能计算是极为关键的。
以Python中的循环完成向量计算
当今的科学和工程问题中,待处理的数据通常表示向量或矩阵,或者说“多维数组”。
假设我们有一个地理信息数据集,它包含大量位置的经纬度信息,我们现在要从中查找出离指定位置最佳的地点。若这些信息以tuple列表保存,我们可以用循环来完成上述功能:
def closest(position, positions):
x0, y0 = position
dbest, ibest = None, None
for i, (x, y) in enumerate(positions):
d = (x - x0) ** 2 + (y - y0) ** 2
if dbest is None or d < dbest:
dbest, ibest = d, i
return ibest
这是最“原始”的Python实现,通过%timeit
来测试它的性能:
import random
positions = [(random.random(), random.random()) for _ in xrange(10000000)]
%timeit closest((.5, .5), positions)
1 loops, best of 3: 7.19 s per loop
整个循环需要7秒多,来看看有无更快的方法。
使用NumPy中的数组
上面的循环中包含大量相同操作(计算与固定点的距离),NumPy提供的多维数组类型非常适合这种场景的需求。
数组的元素类型必须是相同的(data type, dtype)。在多维情形下,元素的存储顺序可有不同选择,如row-major order(C-order)、column-major order(Fortran-order)。NumPy默认采用row-major,但可以在创建数组时指定order。
在性能方面,NumPy由C实现,不需要依赖于Python的循环。此外,现代的CPU实现了向量指令,可使用更大的寄存器(register),从而包含更多的浮点数。当然,这取决于NumPy的编译选项。现在用数组和向量操作重新实现:
positions = rand(10000000, 2)
x, y = positions[:, 0], positions[:, 1]
%timeit distances = (x - 0.5) ** 2 + (y - .5) ** 2
# 10 loops, best of 3: 201 ms per loop
%timeit ibest = distances.argmin()
# 100 loops, best of 3: 10.3 ms per loop
只需要0.21秒左右。
我们有足够的理由说,在数值计算中,尽可能用数组来代替Python循环。