【问题标题】:How can I keep a python object in RAM, without using multiple processes?如何在不使用多个进程的情况下将 python 对象保存在 RAM 中?
【发布时间】:2020-06-24 01:04:21
【问题描述】:

我正在使用一个我无法控制的外部程序,它有时会调用我可以指定的子进程,传递一些参数并在完成后检查其退出状态。

我希望这个程序调用的子进程是一个执行一些计算的 python 3 脚本 使用一个构建起来非常昂贵且非常大的对象。因为对象的构造非常昂贵,所以我不想每次外部程序调用脚本时都构造它,而且因为它需要相当多的空间,所以每次都将它腌制到磁盘并加载它很慢。

我目前对此的解决方案是有两个进程,一个进程构造对象并在请求时执行计算,另一个进程由外部程序调用,其唯一真正目标是通过以下方式与第一个进程通信一个套接字并要求它执行其任务。

第一个进程的代码大致如下:

# process1.py, performs calculations
import socket
import pickle

def do_expensive_calculations(large_object, data):
   # some expensive calculations
   return value

class LargeAndSlow:
    def __init__(self):
        # some code

if __name__ == '__main__':
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    s.bind('ADDRESS')
    s.listen(1)
    
    large_object = LargeAndSlow()

    while True: 
        (conn, address) = s.accept()
        data = pickle.loads(conn.recv(4096))
        value = do_expensive_calculations(large_object, data)
        conn.send(pickle.dumps(value))

第二个(外部程序调用的那个)就像这样:

# process2.py
import sys
import socket
import pickle

def value_is_correct(value):
    # check if value is correct
    return is_correct

if __name__ == '__main__':
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    s.connect('ADDRESS')
    s.send(pickle.dumps(sys.argv))
    
    value = pickle.loads(s.recv(4096))
    if value_is_correct(value):
        s.close()
        sys.exit(0)
    else:
        s.close()
        sys.exit(1)
            

然而,这个解决方案实际上比每次都构造对象要慢,我怀疑这是由于多个 python 进程同时运行并通过 UNIX 套接字进行通信的开销(但是,我可能是错的,也许我只是以一种非常低效的方式编码)。

是否有更快、更有效的解决方案来解决这个问题?

【问题讨论】:

  • 如果对象是可序列化的,您可以将其转储到一个文件中,并在外部程序调用的过程中mmap()它。这样,您的 IPC 就更少了。如果该文件被频繁访问并且有空闲内存,一个不错的操作系统会将文件保存在内存中。

标签: python python-3.x multiprocessing pthreads ram


【解决方案1】:

您可以创建一个 ram 磁盘分区并将其挂载为常规文件系统。在 Unix 系统上,您可以使用 tmpfs 来达到目的。

从程序的角度来看,您的文件看起来就像一个普通文件。不过,操作系统会将其存储在内存中。

请记住,您可能(或可能不会)注意到的速度提升取决于许多因素。 I/O 可能是其中之一,tmpfs 会有所帮助。然而,瓶颈可能是其他东西,例如酸洗/解酸过程。

【讨论】:

  • 这是一个非常好的主意,我试了一下,不幸的是,在这种情况下,解酸过程是瓶颈,但这在某些情况下肯定可以工作。如果没有人提出任何特别适合我的建议,我可以接受这个作为答案。
  • 为了加快酸洗/解酸过程,您可以使用highest protocol。确保所有进程都使用相同版本的 Python。
【解决方案2】:

如果您使用的是 Python 3.8,则 Python 的多处理库已经支持 SharedMemory 类。它可以让多个进程直接访问同一个内存区域。因此,在不同进程中移动大型对象不会产生任何开销。详情可以参考Python's document

如果您使用的不是 Python 3.8,有一个 Python 包SharedArray 具有类似的功能。

【讨论】:

  • 这是一个非常好的主意,但从我在您发布的文档中看到的 SharedMemory 并没有真正提供存储任意对象的方法。有没有办法做到这一点?
  • 您可以使用 pickle 来完成工作。 pickle.dumps 会给你一个字节对象,这样你就可以写入SharedMemory 缓冲区。然后pickle.loads 将从字节串中恢复对象。如果你想节省内存,压缩它并使用最高的pickle协议。您可以使用compress_pickle 包来完成压缩工作。这样,您可以在后台运行一个“存储”进程,并让另一个脚本从“存储”进程中获取共享内存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
  • 1970-01-01
相关资源
最近更新 更多