- 引言
- 方法
- 实验
- 设备
- 结果
- 结论
- 参考
- 其它
- 源码
在上一篇文章中,给大家分享了使用蒙特卡罗法和公式法编写计算圆周率 Pi (π) 的 Python 代码。但发现了一个问题,计算速度很慢。计算圆周率属于计算密集型任务(也叫CPU密集型任务),区别于 IO 密集型任务,即 CPU 占用较大,而数据传输或磁盘等占用较小。对于 CPU 密集型任务,使用多进程能充分使用 CPU 的多个核心,极大地提高计算速度。
方法使用如下公式计算圆周率 Pi (π) 的值: π = ∑ n = 0 ∞ [ 1 1 6 n ( 4 8 n + 1 − 2 8 n + 4 − 1 8 n + 5 − 1 8 n + 6 ) ] \pi = \sum_{n=0}^\infty [\frac{1}{16^n}(\frac{4}{8n+1}-\frac{2}{8n+4}-\frac{1}{8n+5}-\frac{1}{8n+6})] π=n=0∑∞[16n1(8n+14−8n+42−8n+51−8n+61)]
实验 设备8 × Intel® Core™ i5-8300H CPU @ 2.30GHz 2.30 GHz
迭代次数为 10000 时,进程池的不同最大进程数的耗时情况如下:(计算的 PI 值为 3.141592653589793)
进程池的最大进程数耗时(评测 10 次,单位:秒)153.55±1.98242.17±1.49428.01±0.77821.73±0.511619.93±0.483220.51±0.376122.65±0.84备注:进程池的最大进程数必须小于或等于 61,否则会报错。
结论从测试结果来看,进程池的最大进程数为 16 时,计算速度最快,耗时最短,比单进程快了两倍多;为 8 和 32 时,耗时也和 16 相差不多,32 以后耗时就变长了。因此,使用多进程编程时,一般进程池最大进程数设置为 CPU 的核心数就行。
参考https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor
其它多线程是不适合于计算密集型任务的,这里也评测了一下,结果如下:
线程池的最大线程数耗时(仅评测 1 次,单位:秒)148247455 源码import concurrent.futures as cf
from tqdm import tqdm
import os
class CalculatePI(object):
"""Calculate the value of π by multi process
Args:
num_iterations (int): Number of iterations. Default: 100000
max_workers (int): Maximum number of processes. Default: the number of processors on the machine.
"""
def __init__(self, num_iterations: int = 100000, max_workers: int = os.cpu_count()) -> None:
"""Initialization
Args:
num_iterations (int): Number of iterations. Default: 100000
max_workers (int): Maximum number of processes. Default: the number of processors on the machine.
"""
self.num_iterations = num_iterations
self.max_workers = max_workers
def __calc__(self, start: int, end: int) -> float:
"""Calculate the value of π according to formula
Args:
start (int): Starting value for the iterations
end (int): Ending value for the iterations
Returns:
float: Value of π
"""
PI = 0
for n in tqdm(range(start, end)):
PI += 1/pow(16, n) * (4/(8*n+1) - 2 / (8*n+4) - 1/(8*n+5) - 1/(8*n+6))
return PI
def __main__(self) -> float:
"""Calulate the value of π by multi process
Returns:
float: Value of π
"""
PI = 0
with cf.ProcessPoolExecutor(self.max_workers) as p:
futures = []
for i in range(self.max_workers):
start = i*self.num_iterations//self.max_workers
end = (i+1)*self.num_iterations//self.max_workers
futures.append(p.submit(self.__calc__, start, end))
for future in cf.as_completed(futures):
PI += future.result()
return PI
if __name__ == '__main__':
print(CalculatePI().__main__())