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 中的并行计算也存在一些潜在的问题,比如进程间通信、死锁等。在开发并行程序时,需要考虑到这些问题,保证程序的正确性和稳定性。