如何比cPickle更快地将1GB对象反序列化为Python?

时间:2010-11-16 14:32:32

标签: python serialization pickle deserialization

我们有一个基于Python的Web服务器,使用cPickle在启动时取消大量数据文件。数据文件(使用HIGHEST_PROTOCOL腌制)在磁盘上大约为0.4 GB,并加载到内存中的大约1.2 GB的Python对象 - 这需要 20秒。我们在64位Windows机器上使用Python 2.6。

瓶颈肯定不是磁盘(实际读取那么多数据需要不到0.5秒),但是内存分配和对象创建(创建了数百万个对象)。我们希望减少20秒以减少启动时间。

有没有办法将超过1GB的对象反序列化到Python中的速度比cPickle快得多(如5-10x)?因为执行时间受内存分配和对象创建的约束,所以我假设使用另一种类似JSON的unpickling技术在这里没有帮助。

我知道有些解释型语言可以将整个内存映像保存为磁盘文件,因此可以一次性将其加载回内存,而无需为每个对象分配/创建。有没有办法在Python中实现这一目标,或者实现类似的东西?

6 个答案:

答案 0 :(得分:17)

  1. 尝试编组模块 - 它是内部的(由字节编译器使用)并且故意不会广告,但速度要快得多。请注意,它不会序列化任何实例,如pickle,只有内置类型(不记得确切的约束,请参阅docs)。另请注意,格式不稳定。

  2. 如果您需要初始化多个进程并且可以容忍一个始终加载的进程,那么有一个优雅的解决方案:在一个进程中加载​​对象,然后在其中不执行任何操作,除非按需分配进程。分叉很快(写入时复制)并在所有进程之间共享内存。 [免责声明:未经测试; unlike Ruby,Python引用计数将触发页面副本,因此如果您拥有巨大的对象和/或访问其中的一小部分,这可能毫无用处。]

  3. 如果您的对象包含大量原始数据(如numpy数组),则可以对它们进行内存映射,以便更快地启动。 pytables也适用于这些场景。

  4. 如果您只使用一小部分对象,那么OO数据库(如Zope的)可能对您有所帮助。虽然如果你需要它们全部都在内存中,你只会浪费大量开销而不会获得任何收益。 (从未使用过,所以这可能是无稽之谈)。

  5. 也许其他python实现可以做到这一点?不知道,只是一个想法......

答案 1 :(得分:7)

您是否直接从文件加载()腌制数据?如何尝试将文件加载到内存然后进行加载? 我将从尝试cStringIO()开始;或者您可以尝试编写自己的StringIO版本,该版本将使用buffer()来切片内存,这将减少所需的copy()操作(cStringIO仍然可能更快,但您必须尝试)。

特别是在Windows平台上进行这些操作时,有时存在巨大的性能瓶颈; Windows系统在某种程度上是非常不优化的,因为它可以很好地处理大量的小读取。如果load()执行大量小读操作,或者您多次调用load()来读取数据,这将有所帮助。

答案 2 :(得分:4)

我没有使用过cPickle(或Python),但在这种情况下,我认为最好的策略是 避免不必要的对象加载,直到真正需要它们为止 - 比如在另一个线程上启动后加载,实际上它通常更好地避免在任何时候出于显而易见的原因进行不必要的加载/初始化。 Google'延迟加载'或'延迟初始化'。如果您确实需要所有对象在服务器启动之前执行某项任务,那么您可以尝试实现手动自定义反序列化方法,换句话说,如果您对要处理的数据有深入了解,可以自己实现一些可以帮助您“挤压”更好的性能然后是处理它的一般工具。

答案 3 :(得分:3)

您是否尝试过不使用HIGHEST_PROTOCOL来牺牲酸洗的效率?目前尚不清楚使用此协议的性能成本是多少,但值得一试。

答案 4 :(得分:2)

在不了解您正在加载的数据类型以及使用方式的情况下,无法回答这个问题。

如果是某种业务逻辑,也许您应该尝试将其转换为预编译模块;

如果是结构化数据,您可以将其委托给数据库并只提取所需内容吗?

数据是否具有常规结构?有没有办法将它分开并决定什么是必需的,然后才加载它?

答案 5 :(得分:2)

我将添加另一个可能有用的答案 - 如果可以,您是否可以尝试在最常创建的类上定义_ 插槽 _?这可能有点限制且不可能,但它似乎将我的测试初始化​​所需的时间缩短了一半左右。