这个问题可能听起来很简单,但对Python中的并行化不熟悉我肯定会挣扎。我在OpenMP for C ++中处理了并行化,这简直太容易了。 我需要做的是并行修改矩阵的条目。而已。事实是,我不能使用简单的joblib库来做到这一点:
from joblib import Parallel, delayed
my_array = [[ 1 ,2 ,3],[4,5,6]]
def foo(array,x):
for i in [0,1,2]:
array[x][i]=2
return 0
def main(array):
inputs = [0,1]
if __name__ == '__main__':
Parallel(n_jobs=2, verbose = 0)(delayed(foo)(array,i) for i in inputs)
main(my_array)
这个代码在作业数为1时会起作用(所以在调用main之后的数组都是2s),但实际上它变成了真正的多处理时(在调用main之后数组保持不变) )。 我认为的主要问题是我无法使用Parallel函数返回任何有用的东西。我也尝试将数组放在共享内存中,但我找不到有关如何使用joblib执行此操作的任何信息。
有什么建议吗?
答案 0 :(得分:1)
通常有两种共享数据的方式
如果你在Joblib中使用threading
后端,你的当前代码就不会有任何问题,但是在python(Cpython)中不会因为GIL(全局解释器锁)而并行执行。所以这不是真正的并行性
另一方面,使用多个进程,在新进程中生成一个新的解释器,虽然它仍然拥有它自己的GIL,但是你可以产生更多的进程来完成一个特定的任务,这意味着你有一边踩着GIL而你可以同时执行多个操作,问题是进程不共享相同的内存,所以如果他们正在处理特定数据,他们基本上复制这些数据并在自己的副本上工作,而不是一些全局副本,其中每个人都写入类似线程(这就是为什么GIL被放在那里以防止不同的线程意外地改变变量的状态)。共享内存是可能的(不完全共享,你必须在进程之间传递数据)
使用共享内存在Joblib文档中,引用文档
默认情况下,池中的worker是分叉的真正Python进程 使用Python标准库的多处理模块时 n_jobs!= 1.作为并行调用的输入传递的参数是 序列化并重新分配在每个工作进程的内存中。
这意味着默认情况下,如果您没有指定希望在您拥有的进程数之间共享内存,则每个进程将获取列表的副本并对其执行操作(在进行时)现实,你希望他们在你通过的同一个名单上工作。
使用您之前的代码,添加了一些打印语句,您将了解正在进行的操作
from joblib import Parallel, delayed
my_array = [[ 1 ,2 ,3],[4,5,6]]
def foo(array,x):
for i in [0,1,2]:
array[x][i]=2
print(array, id(array), 'arrays in workers')
return 0
def main(array):
print(id(array), 'Original array')
inputs = [0,1]
if __name__ == '__main__':
Parallel(n_jobs=2, verbose = 0)(delayed(foo)(array,i) for i in inputs)
print(my_array, id(array), 'Original array')
main(my_array)
运行代码,我们得到:
140464165602120 Original array
[[2, 2, 3], [4, 5, 6]] 140464163002888 arrays in workers
[[2, 2, 3], [4, 5, 6]] 140464163002888 arrays in workers
[[2, 2, 2], [4, 5, 6]] 140464163002888 arrays in workers
[[1, 2, 3], [2, 5, 6]] 140464163003208 arrays in workers
[[1, 2, 3], [2, 2, 6]] 140464163003208 arrays in workers
[[1, 2, 3], [2, 2, 2]] 140464163003208 arrays in workers
[[1, 2, 3], [4, 5, 6]] 140464165602120 Original array
从输出中你可以看到操作确实在不同的列表(具有不同的id)上执行,因此在一天结束时无法将这两个列表合并在一起以获得所需的结果。 / p>
但是当你指定希望在各个进程之间共享内存时,你会看到输出完全不同,因为进程都在同一个列表上工作
from joblib import Parallel, delayed
from joblib.pool import has_shareable_memory
my_array = [[ 1 ,2 ,3],[4,5,6]]
def foo(array,x):
for i in [0,1,2]:
array[x][i]=2
print(array, id(array), 'arrays in workers')
return 0
def main(array):
print(id(array), 'Original array')
inputs = [0,1]
if __name__ == '__main__':
Parallel(n_jobs=2, verbose = 0)(delayed(has_shareable_memory)(foo(array,i)) for i in inputs)
print(my_array, id(array), 'Original array')
main(my_array)
输出
140615324148552 Original array
[[2, 2, 3], [4, 5, 6]] 140615324148552 arrays in workers
[[2, 2, 3], [4, 5, 6]] 140615324148552 arrays in workers
[[2, 2, 2], [4, 5, 6]] 140615324148552 arrays in workers
[[2, 2, 2], [2, 5, 6]] 140615324148552 arrays in workers
[[2, 2, 2], [2, 2, 6]] 140615324148552 arrays in workers
[[2, 2, 2], [2, 2, 2]] 140615324148552 arrays in workers
[[2, 2, 2], [2, 2, 2]] 140615324148552 Original array
我建议您尝试并使用多处理模块,以便在开始使用Joblib之前真正了解正在发生的事情
https://docs.python.org/3.5/library/multiprocessing.html
使用标准库中的多处理模块的一种简单方法是
from multiprocessing import Pool
def foo(x):
return [2 for i in x]
my_array = [[1 ,2 ,3],[4,5,6]]
if __name__ == '__main__':
with Pool(2) as p:
print(p.map(foo, my_array))
但请注意,这不会修改my_array
,而是返回一个新列表
答案 1 :(得分:1)
仅使用标准库,您可以使用共享内存,在本例中为Array
来存储和修改数组:
from multiprocessing import Pool, Array, Lock
lock = Lock()
my_array = [Array('i', [1, 2, 3], lock=lock),
Array('i', [4, 5, 6], lock=lock),]
让我建议您对您的程序进行一些修改:列出您需要对矩阵进行的所有更改的列表或时间表(为了明确我将使用namedtuple
),以及用于映射这些变化的功能。
Change = namedtuple('Change', 'row idx value')
scheduled_changes = [Change(0, 0, 2),
Change(0, 1, 2),
Change(1, 0 ,2),
Change(1, 1, 2)]
# or build the scheduled changes list in any other way like using
# for loops or list comprehensions...
def modify(change, matrix=my_array):
matrix[change.row][change.idx] = change.value
现在您可以使用Pool
将修改功能映射到更改:
pool = Pool(4)
pool.map(modify, scheduled_changes)
for row in my_array:
for col in row:
print(col, end=' ')
print()
# 2 2 3
# 2 2 6