2013-01-02 78 views
41

我有三个大的列表。首先包含bitarrays(模块bitarray 0.8.0),另外两个包含整数数组。多处理中的共享内存

l1=[bitarray 1, bitarray 2, ... ,bitarray n] 
l2=[array 1, array 2, ... , array n] 
l3=[array 1, array 2, ... , array n] 

这些数据结构需要相当多的RAM(总共约16GB)。

如果我开始使用12个子流程:

multiprocessing.Process(target=someFunction, args=(l1,l2,l3)) 

这是否意味着L1,L2和L3将被复制为每个子流程或将子进程共享这些列表?或者更直接的,我会使用16GB或192GB的RAM吗?

someFunction将从这些列表中读取一些值,然后根据读取的值执行一些计算。结果将返回到父进程。列表l1,l2和l3不会被某些函数修改。

因此,我会假设子流程不需要,也不会复制这些巨大的列表,而是将其与父级分享。这意味着由于linux下的copy-on-write方法,程序会占用16GB的RAM(不管我启动多少个子进程)? 我是否正确或我缺少会导致列表被复制的东西?

编辑: 在读完这个主题后,我仍然感到困惑。一方面,Linux使用copy-on-write,这意味着没有数据被复制。另一方面,访问对象将改变它的引用计数(我仍然不确定为什么以及这意味着什么)。即便如此,整个对象是否会被复制?

例如,如果我限定someFunction如下:

def someFunction(list1, list2, list3): 
    i=random.randint(0,99999) 
    print list1[i], list2[i], list3[i] 

请问使用此函数意味着L1,L2和L3将完全为每个子过程被复制?

有没有办法检查这个?

EDIT2在子进程运行时,读取了更多的内存并监视系统的总内存使用情况后,似乎每个子进程都确实复制了整个对象。这似乎是因为参考计数。

对于l1,l2和l3的引用计数在我的程序中实际上是不需要的。这是因为l1,l2和l3将保留在内存中(不变),直到父进程退出。直到那时,没有必要释放这些列表使用的内存。事实上,我知道引用计数将保持在0以上(对于这些列表以及这些列表中的每个对象),直到程序退出。

所以,现在的问题是,我如何确保对象不会被复制到每个子流程?我可以禁用这些列表和这些列表中的每个对象的引用计数吗?

EDIT3只是一个额外的说明。子流程不需要修改l1,l2l3或这些列表中的任何对象。子流程只需要能够引用这些对象中的一些而不会导致为每个子流程复制存储器。

+0

http://stackoverflow.com/questions/10721915/shared-memory-objects-in-python多处理类似的问题和你的答案。 – sean

+0

重新回到底部,仍然不确定答案。整个对象是否会被复制?只有对象的一部分?只有包含refcount的页面?我如何检查? – FableBlaze

+0

由于抄写时,我认为你不应该做任何特别的事情。为什么不试试呢? – NPE

回答

31

一般来说,有共享相同的数据有两种方式:

  • 多线程
  • 共享内存

Python的多线程不适合CPU密集型任务(因为GIL的),所以通常的解决方案是继续multiprocessing。但是,使用此解决方案,您需要使用multiprocessing.Valuemultiprocessing.Array明确共享数据。

平时注意进程之间共享数据可能不是最好的选择,因为所有的同步问题;涉及演员交换消息的方法通常被认为是更好的选择。也Python documentation参见:

如上所述,做并发编程时它通常是 最好避免使用共享状态尽可能。当使用多个进程时,这是 ,尤其如此。

但是,如果你确实需要使用一些共享的数据,然后 多提供了一组这样的方式。

在你的情况,你需要(通过使用multiprocessing.Array例如)包l1l2l3以某种方式multiprocessing理解,然后将它们作为参数传递。
还要注意的是,如你所说,你不需要写访问,那么你应该通过lock=False而创建的对象,或者所有访问将仍然系列化。

+0

我可以使用'multiprocessing.Array'来包装任意对象的列表,比如'bitarray()'吗? – FableBlaze

+0

@ anti666:我认为你应该使用'multiprocessing.sharedctypes' - 参见http://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.sharedctypes –

+1

或者,如果bitarray支持协议缓冲区,你可以将其作为一个字节阵列共享,然后将其转换回生成的进程中的一个bitarray。 –

9

如果你想使用写入时复制功能,你的数据是静态的(在子进程不变) - 你应该让蟒蛇不乱用您的数据所在的存储块。您可以轻松地通过使用C或C++结构(STL例如)作为容器做到这一点,并提供自己的Python包装将使用指针数据存储器(或可能复制数据MEM)当Python级的对象将如果有的话可以创建。 所有这一切都可以做到非常轻松,几乎蟒蛇简单和语法与cython

 
# pseudo cython 
cdef class FooContainer: 
    cdef char * data 
    def __cinit__(self, char * foo_value): 
     self.data = malloc(1024, sizeof(char)) 
     memcpy(self.data, foo_value, min(1024, len(foo_value))) 

    def get(self): 
     return self.data 

 
# python part 
from foo import FooContainer 

f = FooContainer("hello world") 
pid = fork() 
if not pid: 
    f.get() # this call will read same memory page to where 
      # parent process wrote 1024 chars of self.data 
      # and cython will automatically create a new python string 
      # object from it and return to caller 

上面的伪代码被写的不好。不要使用它。在你的情况下,代替self.data应该是C或C++容器。

0

您可以使用memcached的或Redis的,并设置为每一个键值对 {“L1” ......