如何有效地将已知模式的python dict序列化为二进制?

时间:2017-09-23 04:15:02

标签: python dictionary serialization

我有很多已知架构的python dicts。例如,模式定义为pyspark StructType,如下所示:

from pyspark.sql.types import *
dict_schema = StructType([
        StructField("upload_time", TimestampType(), True),        
        StructField("name", StringType(), True),
        StructField("value", StringType(), True),
    ])

我想有效地将​​每个dict对象序列化为字节数组。什么序列化方法会给我最小的有效载荷?我不想使用pickle,因为有效负载非常大(它将模式嵌入到每个序列化对象中)。

谢谢,

2 个答案:

答案 0 :(得分:0)

您可以使用内置的struct模块。只需“打包”值:

import struct
struct.pack('Q10s5s`, time, name, value)

假设时间是64位int,名称最多10个字符,值最多20个字符。你需要调整它。如果名称和值的长度不一致(您不希望在填充上浪费空间),您可能还会考虑将字符串存储为以空字符结尾的字节序列。

另一个好方法是使用NumPy,假设字符串具有相当一致的长度:

import numpy as np
a = np.empty(1000, [('time', 'u8'), ('name', 'S10'), ('value', 'S20')])
np.save(filename, a)

这将在文件顶部包含各种“模式”;如果你真的想要,你可以编写没有该模式的原始数组。

答案 1 :(得分:0)

我使用msgpackhttps://msgpack.org/)并根据排序的键对字典值进行排序。我使用zip用打包的值重建密钥。

据我所知,msgpack的大小性能通常与struct相当。

就CPU性能而言,msgpack的编码速度要慢10倍,解码速度要慢50%。 msgpack本身可以处理多种格式,并且可以使用JSON之类的编码/解码钩子进行扩展,从而使事情变得更加容易。

在速度方面,...

import time
import msgpack
import struct

_time = int(time.time())
_keys = ('time', 'name', 'value')
_values = (_time, 'name', 'value')

as_msgpack = msgpack.packb(_values, use_bin_type=True)
as_struct = struct.pack('Q10s5s', _time, 'name', 'value')

def test_so_msgpack_encode():
    as_msgpack = msgpack.packb(_values, use_bin_type=True)
    return as_msgpack

def test_so_struct_encode():
    as_struct = struct.pack('Q10s5s', _time, 'name', 'value')
    return as_struct

def test_so_msgpack_decode():
    _decoded = msgpack.unpackb(as_msgpack)
    return dict(zip(_keys, _decoded))

def test_so_struct_decode():
    _decoded = struct.unpack('Q10s5s', as_struct)
    return dict(zip(_keys, _decoded))

print(timeit.timeit("test_so_msgpack_encode()", setup="from __main__ import test_so_msgpack_encode", number=10000))
print(timeit.timeit("test_so_struct_encode()", setup="from __main__ import test_so_struct_encode", number=10000))
print(timeit.timeit("test_so_msgpack_decode()", setup="from __main__ import test_so_msgpack_decode", number=10000))
print(timeit.timeit("test_so_struct_decode()", setup="from __main__ import test_so_struct_decode", number=10000))

就速度而言,虽然有10倍的因素……但这不太可能成为问题。我在具有10年历史(2008年)的计算机上运行了以上示例,以说明10000次以上的迭代:

编码:

0.0745489597321  # msgpack
0.00702214241028  # struct

解码:

0.0458550453186  # msgpack
0.0313770771027  # struct

因此可以通过struct使其运行更快,但是IMHO msgpack提供了更多功能。

注意:以上并非完全相等的检验;我使用了上面的struct unpack结构,似乎将值填充了几个字节。可以固定在包装中或拆开包装,并会影响结果。