这篇文章介绍我所知的一些提升 Python 的科学计算性能的办法。

一、多使用 NumPy 库

Python 的列表用于处理数值计算十分低效,而 NumPy 库提供的各种运算函数,由于底层使用 C++ 实现,并且使用了一些向量与矩阵运算的加速技巧,运算速度比列表要快上不少。下面用一个例子来体现它们的速度差。

这个 Python 脚本制造了一个长度为一百万的纯数字列表,将其所有的元素加 2,然后计算出之后的最小值,并且输出结果和运算所使用的总时间。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time

# Start ticking.

start_time = time.time()

# Define the array, and print its length.

array = list(range(1000000))
print('The length of the array is ', len(array))

# Increase each element by 2.

for i in range(len(array)):
    array[i] += 2

# Find the minimum and print it.

min = min(array)
print('The minimum is ', min)

# Print the elapsed time.

end_time = time.time()
print('Elapsed time is ', end_time - start_time)

输出的结果为:

1
2
3
The length of the array is  1000000
The minimum is  2
Elapsed time is  0.12588071823120117

这说明它的运行大概要花费 0.12 秒。那么,如果稍微修改一下它,使用 NumPy 库的数组来替换原脚本中的列表,更新后的脚本如下(注意高亮的行):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time
import numpy as np

# Start ticking.

start_time = time.time()

# Define the array, and print its length.

array = np.arange(1000000)
print('The length of the array is ', len(array))

# Increase each element by 2.

array += 2

# Find the minimum and print it.

min = np.min(array)
print('The minimum is ', min)

# Print the elapsed time.

end_time = time.time()
print('Elapsed time is ', end_time - start_time)

运行这个脚本,输出为:

1
2
3
The length of the array is  1000000
The minimum is  2
Elapsed time is  0.002527952194213867

速度直接从 0.12 秒,提升到了 0.002 秒,对比已经非常明显了。虽然没人会直接去用 Python 原生列表去做科学计算,可是就我自己而言,有时候有些简单的小功能,为了图省事,可能就直接用列表去实现了。列表的低效叠加起来,可能就会给程序的运行效率带来许多负面影响。

二、操作系统很重要

在我自己实际做项目的过程中,我注意到,在 Windows 和 Linux 上,Python 的计算效率有非常大的区别。我在同一台电脑(八代十二核 i7 处理器,16 GB 内存,集成显卡)上,安装了 Windows 10 和 Arch Linux 操作系统,但是在它们上分别运行一样的代码,速度能相差好几倍(大概是不到十倍,自己的粗略估计,没有精确计时对比)。因此,我现在只在 Linux 上跑 Python 计算了。但是,这只是个人经验

我也在搜索引擎上进行了一些调查,得到的结论是:Python 在 Linux 上的确比 Windows 要快很多,而且是 much faster,快得相当多(当然,许多网上的说法也只是基于经验),但是NumPy 在 Linux 上的性能并没有明显高于 Windows(参考官方的性能测试)。

我的 Arch Linux 是自己从最基本的命令行界面一点点“组装”起来的,哪怕和同为 Linux 的 Ubuntu 相比,它都要轻量许多,因此自然成了我跑计算程序的首选操作系统。

我没有 mac 电脑,所以没有测试它的性能。有机会的话一定也要测试一下。

三、并行计算、GPU 计算等

这一点感谢实验室的 ZK 师兄给我提供思路。可惜我对于这两点的理解都不够多,这里只能提供少许信息。

并行计算,可以使用 Python 内置的 multiprocessing 包 来实现。这里借用这个网站上的例子来展示它的基本用法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import multiprocessing

def worker():
    """worker function"""
    print 'Worker'
    return

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker)
        jobs.append(p)
        p.start()
1
2
3
4
5
6
7
$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker

使用 GPU 做计算,是现在非常火的方向。这里仅提供一篇 NVIDIA 提供的在 Python 中使用 GPU 加速计算的教程