Numpy在Python中的多进程使用

Numpy在Python中的多进程使用

在本文中,我们将介绍在Python中使用Numpy库进行多进程计算的方法,并利用click.progressbar来实现进度条的显示。

阅读更多:Numpy 教程

Numpy多进程计算

Numpy是Python中非常常用的科学计算库,它提供了各种方便的函数和数据结构,可以加快我们的计算速度。随着计算量的增加,不少同学发现使用Numpy的计算速度还不够快,于是考虑使用多进程并行计算来提升速度。

Python中使用多进程可使用multiprocessing库。具体使用方法如下:

from multiprocessing import Pool

def f(x):
    return x**2

if __name__ == '__main__':
    with Pool(5) as p:
        result = p.map(f, [1, 2, 3, 4, 5])
    print(result)

上述代码使用了multiprocessing.Pool来创建5个进程,并使用map函数将[1,2,3,4,5]作为参数传入进程中进行平方计算。这样可以加快计算速度。

使用Numpy的多进程计算

在使用Numpy进行多进程计算时,我们需要将操作尽可能向量化。例如,下面是对两个矩阵点乘的向量化代码:

import numpy as np

def dot(x, y):
    return np.sum(x * y)

if __name__ == '__main__':
    x = np.random.rand(100, 1000)
    y = np.random.rand(1000, 100)
    z = np.zeros((100, 100))
    with Pool(5) as p:
        result = p.starmap(dot, [(x[i,:], y[:,j]) for i in range(100) for j in range(100)])
    for i in range(100):
        for j in range(100):
            z[i,j] = result[i*100+j]
    print(z)

上面的代码中,我们使用了starmap函数,将参数以元组的形式传入函数中。由于使用了map函数的话,每个进程中的计算量是平均的,而很多情况下,矩阵中的元素不是均匀分布的,所以使用starmap函数可以更好地调整并发计算任务的大小。

不过需要注意的是,使用进程时需要考虑队列,如果我们不及时读取队列中的元素,就会出现进程无法正常结束的情况,这是因为队列已经满了。我们可以使用p.close()将不再接受新任务并正常退出,使用p.terminate()可以立即停止所有进程。

进度条

如果我们使用了进程池来进行大量计算,那么会发现运行起来非常慢,而且没有进度条的提示,无法知道程序是否还在正常运行。这时我们可以使用click库来打印进度条。

click是一个非常好用的命令行工具库,可以帮助我们方便地创建命令行程序,其中就包含了进度条的功能。

我们可以通过以下的函数来创建进度条:

import click

with click.progressbar(range(1000000)) as bar:
    for i in bar:
        pass

上述代码中,我们使用click.progressbar函数来创建一个进度条,并将元素个数设置为1000000,程序会依次遍历这1000000个元素并输出进度条。

当我们使用进程池的时候,进度条就不能像上述一样简单地使用了。我们需要自己手动更新进度条。具体方法如下:

import numpy as np
from multiprocessing import Pool
import time
import click

def dot(x, y, idx):
    return np.sum(x*y), idx

def callback(result):
    bar.update(1)

if __name__ == '__main__':
    x= np.random.rand(1000, 1000)
    y = np.random.rand(1000, 1000)
    bar = click.progressbar(length=1000, label='computing')
    results = np.zeros_like(x)
    with Pool(processes=8) as pool:
        for i in range(x.shape[0]):
            for j in range(x.shape[1]):
                pool.apply_async(dot, args=(x[i,j], y[i,j], (i,j)), callback=callback)
        pool.close()
        pool.join()
    bar.render_finish()

上述代码中,我们首先创建了一个进度条并将长度设置为1000,随后,我们使用了multiprocessing.Pool类来创建8个进程,之后循环遍历x数组中的元素,每个元素都调用pool.apply_async函数来异步向进程池中添加任务,并且在添加任务时设置了回调函数callback。最后关闭该进程池并等待所有进程结束。

在完成了每个任务的时候,回调函数会被执行一次,此时我们可以执行bar.update(1)来更新进度条。

值得注意的是,每次调用bar.update(1)都会刷新屏幕,所以在任务量很大的时候我们不应该每次更新,否则会导致屏幕刷新过于频繁,影响用户体验。我们可以在一定条件下更新进度条,例如每完成100个任务时进行一次更新:if idx[1] % 100 == 0: bar.update(100)

总结

本文介绍了Numpy在Python中的多进程使用方法,并且通过使用click.progressbar展示了进度条。同时,在使用进程时需要注意队列大小以免进程无法正常结束,同时在更新进度条时要优化更新时机以获得更好的用户体验。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程