如何使用并行计算加速Python程序的运行

1. 简介

Python 是一种高级编程语言,它拥有极高的可读性和易用性,常被誉为“胶水语言”。然而,由于 Python 是一种解释性语言,对于一些耗时的计算任务,运行速度却往往难以满足需求。在这种情况下,通过并行计算来加速运行速度,变得非常必要。

2. Python 的并行计算

在 Python 中,实现并行计算的方式非常丰富,有多进程、多线程、协程、异步 IO 等等。在这里,我们将介绍使用 multiprocessing 模块进行并行计算。

2.1 multiprocessing 模块

multiprocessing 是 Python 自带的一个多进程模块,它允许程序员在 Python 中开启子进程,在这些子进程中运行代码。这些子进程可以利用 CPU 的多核心来提高计算速度。与多线程不同,多进程可以充分地利用多核 CPU 的优势,避免 GIL (全局解释器锁)的影响,从而实现更高效的计算。

2.2 使用 multiprocessing 模块进行并行计算

下面我们来看一个例子,假设有一个要计算的函数 func(),需要传入一个参数 x,计算结果为 y。

def func(x):

y = x * x

return y

result = func(3)

print(result)

如果我们要计算多个 x 的值对应的 y,传统的做法是循环计算:

x_list = [1, 2, 3, 4, 5]

result_list = []

for x in x_list:

y = func(x)

result_list.append(y)

print(result_list)

这种方式的计算速度将受到一个循环内代码的运行时间限制,无法充分利用计算机的多核 CPU 跑满计算能力。

现在,我们使用 multiprocessing 模块来实现多进程计算,将计算过程并行化:

from multiprocessing import Pool

def func(x):

y = x * x

return y

x_list = [1, 2, 3, 4, 5]

pool = Pool(processes=4) # 开启 4 个进程

result_list = pool.map(func, x_list)

print(result_list)

上述代码中,我们首先将需要计算的 x 值存储在列表中,然后开启一个进程池,通过 map() 方法将计算任务分配到不同的进程中进行计算,最终将所有的计算结果存储在 result_list 列表中。这种方法大大提高了计算效率。

3. 示例

3.1 顺序计算

首先,我们定义一个可以计算图像中每个像素灰度值的函数:

from PIL import Image

def process_image(image_path):

im = Image.open(image_path)

im = im.convert("L")

im = im.rotate(45, expand=True)

im = im.resize((800, 600))

return im

在这个函数中,我们加载了一张图片,将其转换为灰度图,旋转 45 度,并最终缩放至指定大小。

接着,我们定义一个函数,用来对一个文件夹中的所有图片进行顺序计算:

from os import listdir, path

def process_folder_sequential(folder_path):

files = listdir(folder_path)

result_list = []

for file in files:

file_path = path.join(folder_path, file)

result = process_image(file_path)

result_list.append(result)

return result_list

这个函数内部,我们首先通过 listdir() 方法,列出文件夹内的所有文件,然后针对每个文件,调用 process_image() 函数进行计算,并把结果添加到一个列表中。最终返回该列表。

我们现在运行程序,对一个包含 10 张图片的文件夹进行计算,记录计算耗时:

from datetime import datetime

start_time = datetime.now()

folder_path = "images"

result_list = process_folder_sequential(folder_path)

for result in result_list:

result.show()

end_time = datetime.now()

delta = end_time - start_time

print("Elapsed time: ", delta.total_seconds())

由于要逐张加载图片并进行计算,所以程序运行较慢。针对本机环境,大约需要花费 10 秒钟的时间。

3.2 并行计算

接下来,我们使用 multiprocessing 模块来实现并行计算。将 process_folder_sequential() 函数修改为如下形式:

def process_folder_parallel(folder_path):

files = listdir(folder_path)

pool = Pool(processes=4)

result_list = pool.map(process_image, [path.join(folder_path, file) for file in files])

return result_list

我们首先列出了文件夹内的所有文件,然后开启了一个进程池,将每一张图片的计算任务分配到不同的进程中进行计算,同时实例化了 4 个进程。最终,我们将所有计算结果存储在 result_list 列表中,并将其返回。

接下来运行程序,使用 process_folder_parallel() 函数对文件夹进行计算,记录计算耗时。

start_time = datetime.now()

folder_path = "images"

result_list = process_folder_parallel(folder_path)

for result in result_list:

result.show()

end_time = datetime.now()

delta = end_time - start_time

print("Elapsed time: ", delta.total_seconds())

我们通过并行化处理,计算 10 张图片的时间缩短到了 3 秒钟左右,运行速度有了很大的提升。

4. 总结

通过使用 multiprocessing 模块,我们可以方便地实现 Python 中的并行计算,从而提高计算效率。通过上面的示例可以看到,优化之后程序的运行速度显著提高。

不过需要注意,Python 中的并行计算也存在一些潜在的问题,比如进程间通信、死锁等。在开发并行程序时,需要考虑到这些问题,保证程序的正确性和稳定性。

后端开发标签