python - 在 Python 中对 Cython 对象进行空间高效的 pickle ?

标签 python struct cython pickle namedtuple

我正在尝试找到一种节省空间的方法来在 Python 中存储类似结构的对象。

# file point.py

import collections
Point = collections.namedtuple('Point', ['x', 'y'])

这是 cythonized 版本:

# file cpoint.pyx

cdef class CPoint:

    cdef readonly int x
    cdef readonly int y

    def __init__(self, int x, int y):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Point(x={}, y={})'.format(self.x, self.y)

我希望 cythonized 版本的内存效率更高:

from pympler.asizeof import asizeof
from point import Point
from cpoint import CPoint

asizeof(Point(1,2))     # returns 184
asizeof(CPoint(1,2))    # returns 24

但令人惊讶的是,尽管有静态类型和更轻的内存表示,但 cythonized 版本在 pickled 时占用了更多空间。

import pickle
len(pickle.dumps(Point(1,2)))     # returns 28
len(pickle.dumps(CPoint(1,2)))    # returns 70

是否有更有效的方法来序列化这样的 cython 对象?


后续

我想要保留各个 CPoint 对象的原因是因为我在流应用程序中接收异构 CPoint 类对象,因此我需要缓冲它们位于异构类型的列表中。

如果我们能保证列表元素的类型,那么确实可以使用 numpy 数组来改善存储空间。我们也有可能通过同质容器获得更好的压缩属性,但您必须放弃序列化非结构化数据的多功能性。

在容纳非结构化数据的同时,依靠@ead和@DavidW提出的同质容器的空间优势的一种算法解决方案是存储前面对象位置的位图(假设我们知道所有可能的类型)在字节码编译时传入对象(这是一个广泛的假设),然后仍然将对象分组在同质容器中。也许可以通过以面向列的方式对它们进行排序来进一步提高效率,以便压缩可以更好地提高效率。如果没有基准测试,很难说。

最佳答案

这并不是专门的 Cython 解决方案,但是:如果您担心磁盘上的大小,那么您可能有很多这样的解决方案。在这种情况下,一个不错的选择是将数据存储在 numpy structured array 中。以避免创建大量 Python 对象(或者可能是 Pandas 之类的对象)。

我还希望对对象的数组/numpy 列表进行pickle 比对单个对象进行pickle 更有用(我相信当你有很多对象时,pickle 会进行一些优化)同样的事情)

import collections
from cpoint import CPoint

Point = collections.namedtuple('Point', ['x', 'y'])

l = [ Point(n,n) for n in range(10000) ]
l2 = [ CPoint(n,n) for n in range(10000) ]

import numpy as np
l3 = np.array(list(zip(list(range(10000)), list(range(10000)))),
              dtype=[('x',int),('y',int)])

import pickle
print("Point",len(pickle.dumps(l))/20000)
print("CPoint",len(pickle.dumps(l2))/20000)
print("nparray",len(pickle.dumps(l3))/20000)

打印:

Point 9.9384

CPoint 16.4402

nparray 8.01215

namedtuplenumpy.array 版本都非常接近我们期望的每个 int 8 字节的限制,但 numpy 数组版本更好。


有趣的是,如果我们在调用中添加 protocol=pickle.HIGHEST_PROTOCOL ,那么一切都会进一步改善,并且 namedtuple 版本再次令人信服地获胜。 (我怀疑它注意到它不需要完整的 64 位整数来存储,并且我怀疑这是否很容易手动击败)

Point 5.9775

CPoint 10.47975

nparray 8.0107

关于python - 在 Python 中对 Cython 对象进行空间高效的 pickle ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50660112/

相关文章:

c++ - 在结构 vector 中初始化一个 vector

c - 没有 typedef 关键字的结构

c - 初始化结构指针

python - Cython 和 gcc : can't run compiled program

python - 将 Anaconda MKL 链接到 C++/Cython 程序

python - cython 路径难以编译 pyx pxd 文件的工作版本

python - 用索引处的先前值填充 0

python - 如何找出 sklearn 决策树的大小?

java - Python 中是否有类似 Java IllegalStateException 的东西?

exception - 在 Python 2 和 Python3 上重新引发 Cython 中的异常