Python 多处理:在进程之间共享大型只读对象?
子进程是否通过共享程序中先前创建的对象生成 我有以下设置:Python 多处理:在进程之间共享大型只读对象?,python,multiprocessing,Python,Multiprocessing,子进程是否通过共享程序中先前创建的对象生成 我有以下设置: do_some_processing(filename): for line in file(filename): if line.split(',')[0] in big_lookup_object: # something here if __name__ == '__main__': big_lookup_object = marshal.load('file.bin')
do_some_processing(filename):
for line in file(filename):
if line.split(',')[0] in big_lookup_object:
# something here
if __name__ == '__main__':
big_lookup_object = marshal.load('file.bin')
pool = Pool(processes=4)
print pool.map(do_some_processing, glob.glob('*.data'))
我正在将一些大对象加载到内存中,然后创建一个需要使用该大对象的工作人员池。大对象是只读访问的,我不需要在进程之间传递对它的修改
我的问题是:大对象是否像我在unix/c中生成一个进程那样加载到共享内存中,还是每个进程都加载自己的大对象副本
更新:进一步澄清-大查找对象是一个共享查找对象。我不需要把它分开单独处理。我需要保留一份副本。我需要拆分它的工作是读取大量其他大文件,并根据查找对象查找这些大文件中的项
进一步更新:数据库是一个很好的解决方案,memcached可能是一个更好的解决方案,而磁盘上的文件(shelve或dbm)可能更好。在这个问题上,我对内存解决方案特别感兴趣。对于最终的解决方案,我将使用hadoop,但我想看看是否也可以使用本地内存版本
通过多处理生成的子进程是否共享程序中先前创建的对象
否(python 3.8之前的版本),以及
进程有独立的内存空间
解决方案1
要充分利用一个有大量工人的大型结构,请这样做
stdin
读取中间结果,执行操作,在stdout
上写入中间结果
process1 <source | process2 | process3 | ... | processn >result
process1结果
解决方案2 在某些情况下,您有一个更复杂的结构-通常是扇出结构。在这种情况下,您有一个具有多个子项的父项
sys.stdin
父母在繁殖所有的孩子和正确地保留管道方面有一点花哨的步法,但这并不太糟糕
扇入是相反的结构。许多独立运行的进程需要将它们的输入交织到一个公共进程中。收集器并不容易编写,因为它必须从许多源读取
通常使用select
模块读取许多命名管道,以查看哪些管道具有挂起的输入
解决方案3 共享查找是数据库的定义 解决方案3A–加载数据库。让工作人员处理数据库中的数据 解决方案3B–创建一个非常简单的服务器,使用(或类似)提供响应HTTP GET的WSGI应用程序,以便工作人员可以查询服务器
解决方案4 共享文件系统对象。Unix操作系统提供共享内存对象。这些只是映射到内存的文件,以便交换I/O,而不是进行更多约定缓冲读取 可以通过多种方式从Python上下文中执行此操作
seek
操作编写一个页面结构的字节编码文件,以确保通过简单的seek可以轻松找到各个部分。这就是数据库引擎所做的–将数据分解为多个页面,通过搜索
轻松定位每个页面
生成具有此大型页面结构文件访问权限的工作人员。每个工作人员都可以找到相关部件并在那里工作。不同的进程有不同的地址空间。比如运行解释器的不同实例。这就是IPC(进程间通信)的用途 为此,您可以使用队列或管道。如果以后要通过网络分发进程,也可以使用tcp上的rpc
如果您在Unix下运行,它们可能共享同一对象,原因是(即子进程有单独的内存,但它是写时复制的,因此只要没有人修改它,就可以共享)。我尝试了以下方法:
import multiprocessing
x = 23
def printx(y):
print x, id(x)
print y
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=4)
pool.map(printx, (1,2,3,4))
并得到以下输出:
$ ./mtest.py
23 22995656
1
23 22995656
2
23 22995656
3
23 22995656
4
美元/公吨/年
23 22995656
1.
23 22995656
2.
23 22995656
3.
23 22995656
4.
当然,这并不能证明没有制作副本,但在您的情况下,您应该能够通过查看ps
的输出来验证这一点,以查看每个子进程使用了多少实际内存。是正确的。Python的多处理快捷方式有效地为您提供了一个单独的、重复的内存块
在大多数*nix系统上,使用对os.fork()
的较低级别调用实际上会给您提供写时拷贝内存,这可能就是您的想法。好吧,理论上,在最简单的程序中,你可以从数据中读取数据而不需要复制
然而,在Python解释器中事情并不是那么简单。对象数据和元数据存储在同一内存段中,因此即使对象从未更改,该对象的引用计数器之类的内容也会增加,从而导致内存写入
#!/usr/bin/env python
import os, sys, time
from multiprocessing import Pool
x = 23000 # replace `23` due to small integers share representation
z = [] # integers are immutable, let's try mutable object
def printx(y):
global x
if y == 3:
x = -x
z.append(y)
print os.getpid(), x, id(x), z, id(z)
print y
if len(sys.argv) == 2 and sys.argv[1] == "sleep":
time.sleep(.1) # should make more apparant the effect
if __name__ == '__main__':
pool = Pool(processes=4)
pool.map(printx, (1,2,3,4))
$ python26 test_share.py sleep
2504 23000 11639492 [1] 10774408
1
2564 23000 11639492 [2] 10774408
2
2504 -23000 11639384 [1, 3] 10774408
3
4084 23000 11639492 [4] 10774408
4
$ python26 test_share.py
1148 23000 11639492 [1] 10774408
1
1148 23000 11639492 [1, 2] 10774408
2
1148 -23000 11639324 [1, 2, 3] 10774408
3
1148 -23000 11639324 [1, 2, 3, 4] 10774408
4
import time
import multiprocessing
def load_data( queue_load, n_processes )
... load data here into some_variable
"""
Store multiple copies of the data into
the data queue. There needs to be enough
copies available for each process to access.
"""
for i in range(n_processes):
queue_load.put(some_variable)
def work_with_data( queue_data, queue_load ):
# Wait for load_data() to complete
while queue_load.empty():
time.sleep(1)
some_variable = queue_load.get()
"""
! Tuples can also be used here
if you have multiple data files
you wish to keep seperate.
a,b = queue_load.get()
"""
... do some stuff, resulting in new_data
# store it in the queue
queue_data.put(new_data)
def start_multiprocess():
n_processes = 5
processes = []
stored_data = []
# Create two Queues
queue_load = multiprocessing.Queue()
queue_data = multiprocessing.Queue()
for i in range(n_processes):
if i == 0:
# Your big data file will be loaded here...
p = multiprocessing.Process(target = load_data,
args=(queue_load, n_processes))
processes.append(p)
p.start()
# ... and then it will be used here with each process
p = multiprocessing.Process(target = work_with_data,
args=(queue_data, queue_load))
processes.append(p)
p.start()
for i in range(n_processes)
new_data = queue_data.get()
stored_data.append(new_data)
for p in processes:
p.join()
print(processes)