是否值得使用IPython与scipy的eig并行?
作者:互联网
我正在编写一个必须计算大量特征值问题的代码(典型的矩阵维数是几百个).我想知道是否可以通过使用IPython.parallel模块加快进程.作为一名前MATLAB用户和Python新手,我正在寻找类似于MATLAB的parfor …
在一些在线教程之后,我写了一个简单的代码来检查它是否加速了计算,我发现它没有,并且经常实际上减慢它(取决于案例).我想,我可能会错过它中的一点,也许scipy.linalg.eig以这样一种方式实现,即它使用所有可用的内核,并尝试并行化它,我中断了引擎管理.
这是’parralel’代码:
import numpy as np
from scipy.linalg import eig
from IPython import parallel
#create the matrices
matrix_size = 300
matrices = {}
for i in range(100):
matrices[i] = np.random.rand(matrix_size, matrix_size)
rc = parallel.Client()
lview = rc.load_balanced_view()
results = {}
#compute the eigenvalues
for i in range(len(matrices)):
asyncresult = lview.apply(eig, matrices[i], right=False)
results[i] = asyncresult
for i, asyncresult in results.iteritems():
results[i] = asyncresult.get()
非并行变体:
#no parallel
for i in range(len(matrices)):
results[i] = eig(matrices[i], right=False)
两者的CPU时间差异非常微妙.如果在特征值问题之上,并行化函数必须进行更多的矩阵运算,则它开始永远持续,即比非并行化变量至少长5倍.
我是对的,特征值问题不适合这种并行化,还是我错过了重点?
非常感谢!
2013年7月29日编辑; 12:20 BST
在moarningsun的建议之后,我尝试在修复mkl.set_num_threads的线程数的同时运行eig.对于500乘500的矩阵,最小50次重复设置如下:
No of. threads minimum time(timeit) CPU usage(Task Manager)
=================================================================
1 0.4513775764796151 12-13%
2 0.36869288559927327 25-27%
3 0.34014644287680085 38-41%
4 0.3380558903450037 49-53%
5 0.33508234276183657 49-53%
6 0.3379019065051807 49-53%
7 0.33858615048501406 49-53%
8 0.34488405094054997 49-53%
9 0.33380300334101776 49-53%
10 0.3288481198342197 49-53%
11 0.3512653110685733 49-53%
除了一个线程的情况,没有实质性的差异(可能50个样本有点小…).我仍然认为我错过了这一点,并且可以做很多事情来提高性能,但不确定如何.
这些是在4核计算机上运行的,启用了超线程,可提供4个虚拟核心.
感谢您的任何意见!
解决方法:
有趣的问题.因为我认为应该可以实现更好的缩放,我用一个小的“基准”调查了性能.通过这个测试,我将单线程和多线程eig(通过MKL LAPACK / BLAS例程提供的多线程)的性能与IPython并行化eig进行了比较.为了看看它会有什么不同,我改变了视图类型,引擎数量和MKL线程,以及在引擎上分配矩阵的方法.
以下是旧AMD双核系统的结果:
m_size=300, n_mat=64, repeat=3
+------------------------------------+----------------------+
| settings | speedup factor |
+--------+------+------+-------------+-----------+----------+
| func | neng | nmkl | view type | vs single | vs multi |
+--------+------+------+-------------+-----------+----------+
| ip_map | 2 | 1 | direct_view | 1.67 | 1.62 |
| ip_map | 2 | 1 | loadb_view | 1.60 | 1.55 |
| ip_map | 2 | 2 | direct_view | 1.59 | 1.54 |
| ip_map | 2 | 2 | loadb_view | 0.94 | 0.91 |
| ip_map | 4 | 1 | direct_view | 1.69 | 1.64 |
| ip_map | 4 | 1 | loadb_view | 1.61 | 1.57 |
| ip_map | 4 | 2 | direct_view | 1.15 | 1.12 |
| ip_map | 4 | 2 | loadb_view | 0.88 | 0.85 |
| parfor | 2 | 1 | direct_view | 0.81 | 0.79 |
| parfor | 2 | 1 | loadb_view | 1.61 | 1.56 |
| parfor | 2 | 2 | direct_view | 0.71 | 0.69 |
| parfor | 2 | 2 | loadb_view | 0.94 | 0.92 |
| parfor | 4 | 1 | direct_view | 0.41 | 0.40 |
| parfor | 4 | 1 | loadb_view | 1.62 | 1.58 |
| parfor | 4 | 2 | direct_view | 0.34 | 0.33 |
| parfor | 4 | 2 | loadb_view | 0.90 | 0.88 |
+--------+------+------+-------------+-----------+----------+
如您所见,不同设置的性能增益差别很大,最多为常规多线程eig的1.64倍.在这些结果中,除非在引擎上禁用MKL线程(使用view.apply_sync(mkl.set_num_threads,1)),否则您使用的parfor函数表现不佳.
改变矩阵大小也给出了值得注意的差异.在具有4个引擎的direct_view上使用ip_map并且禁用MKL线程与常规多线程eig的加速:
n_mat=32, repeat=3
+--------+----------+
| m_size | vs multi |
+--------+----------+
| 50 | 0.78 |
| 100 | 1.44 |
| 150 | 1.71 |
| 200 | 1.75 |
| 300 | 1.68 |
| 400 | 1.60 |
| 500 | 1.57 |
+--------+----------+
显然对于相对较小的矩阵存在性能损失,对于中间大小,加速是最大的,对于较大的矩阵,加速再次降低.在我看来,我可以获得1.75的性能提升,这将使得使用IPython.parallel值得.
我之前在英特尔双核笔记本电脑上做过一些测试,但我得到了一些有趣的结果,显然笔记本电脑过热了.但在该系统上,加速一般要低一些,最高可达1.5-1.6.
现在我认为你的问题的答案应该是:这取决于.性能增益取决于硬件,BLAS / LAPACK库,问题大小以及IPython.parallel的部署方式,以及其他可能我不知道的事情.最后但并非最不重要的是,它是否值得,还取决于您认为值得多大的性能提升.
我使用的代码:
from __future__ import print_function
from numpy.random import rand
from IPython.parallel import Client
from mkl import set_num_threads
from timeit import default_timer as clock
from scipy.linalg import eig
from functools import partial
from itertools import product
eig = partial(eig, right=False) # desired keyword arg as standard
class Bench(object):
def __init__(self, m_size, n_mat, repeat=3):
self.n_mat = n_mat
self.matrix = rand(n_mat, m_size, m_size)
self.repeat = repeat
self.rc = Client()
def map(self):
results = map(eig, self.matrix)
def ip_map(self):
results = self.view.map_sync(eig, self.matrix)
def parfor(self):
results = {}
for i in range(self.n_mat):
results[i] = self.view.apply_async(eig, self.matrix[i,:,:])
for i in range(self.n_mat):
results[i] = results[i].get()
def timer(self, func):
t = clock()
func()
return clock() - t
def run(self, func, n_engines, n_mkl, view_method):
self.view = view_method(range(n_engines))
self.view.apply_sync(set_num_threads, n_mkl)
set_num_threads(n_mkl)
return min(self.timer(func) for _ in range(self.repeat))
def run_all(self):
funcs = self.ip_map, self.parfor
n_engines = 2, 4
n_mkls = 1, 2
views = self.rc.direct_view, self.rc.load_balanced_view
times = []
for n_mkl in n_mkls:
args = self.map, 0, n_mkl, views[0]
times.append(self.run(*args))
for args in product(funcs, n_engines, n_mkls, views):
times.append(self.run(*args))
return times
Dunno如果重要但是要启动我在命令行输入的4个IPython并行引擎:
ipcluster start -n 4
希望这可以帮助 :)
标签:python,scipy,eigenvalue,ipython-parallel 来源: https://codeday.me/bug/20190629/1326173.html