如何在有充足RAM的情况下加速大型对象的反序列化?

28 投票
8 回答
22058 浏览
提问于 2025-04-15 22:20

我用cPickle读取一个1GB的NetworkX图数据结构,竟然要花我将近一个小时(这个文件在磁盘上以二进制pickle文件存储时就是1GB)。

不过,这个文件加载到内存里是很快的。换句话说,如果我运行:

import cPickle as pickle

f = open("bigNetworkXGraph.pickle","rb")
binary_data = f.read() # This part doesn't take long
graph = pickle.loads(binary_data) # This takes ages

我该怎么加快最后这个操作的速度呢?

我注意到我尝试过用二进制协议(1和2)来打包数据,但似乎用哪种协议都没什么太大区别。另外,虽然我上面使用的是“loads”(意思是“加载字符串”)这个函数,但它实际上是在加载二进制数据,而不是ascii数据。

我使用的系统有128GB的内存,所以我希望有人能告诉我怎么增加一些在pickle实现中隐藏的读取缓冲区。

8 个回答

4

你可以试试把你的数据进行序列化,然后用像memcached这样的工具把它存储在内存中。虽然这种方法有一些限制,但正如这篇文章所说,序列化的速度比另一种方法(叫做pickle)快得多,大约快20到30倍。

当然,你还应该花时间优化你的数据结构,这样可以减少你需要存储的数据量和复杂性。

13

我成功地用cPickle读取了一个大约750 MB的igraph数据结构(一个二进制的pickle文件)。这个过程很简单,只需要像这里提到的那样,封装一下pickle的加载调用就可以了,具体可以参考这里

在你的情况下,示例代码可能看起来像这样:

import cPickle as pickle
import gc

f = open("bigNetworkXGraph.pickle", "rb")

# disable garbage collector
gc.disable()

graph = pickle.load(f)

# enable garbage collector again
gc.enable()
f.close()

虽然这并不是最完美的方法,但确实大大缩短了所需的时间。
(对我来说,时间从843.04秒减少到了41.28秒,差不多快了20倍)

8

你可能是因为创建和分配Python对象的开销太大,而不是因为反序列化(unpickling)本身的问题。 如果真是这样,那你能做的事情不多,除了不一次性创建所有对象。你真的需要一次性加载整个结构吗?如果不需要的话,可以考虑懒加载数据结构(比如:用被序列化的字符串来表示结构的一部分,只有在需要的时候才进行反序列化)。

撰写回答