首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >泡菜协议2和3之间的巨大差异(以字节为单位)

泡菜协议2和3之间的巨大差异(以字节为单位)
EN

Stack Overflow用户
提问于 2014-10-22 19:12:41
回答 1查看 1.7K关注 0票数 3

流线端继续发送一个2048字节的声音样本,并将时间作为整数一起发送到一个用pickle.dumps进行腌制的元组中,然后发送一个UDP数据包给接收方,然后接收方对其进行解压缩、缓冲,然后播放声音样本。

使用python 3一切都很好,接收端的位/秒速度是预期的。

当我在python2.7中运行流光时,速度更快了!我坚韧的蟒蛇2不知何故更快。

然后,我向wireshark查询了接收方接收的UDP数据包,它们比需要的要大。

流光面:

代码语言:javascript
复制
while True:
    data = next(gen)
    print("data:{}".format(len(data)))
    stime +=1
    msg = (stime,data)
    payload = pickle.dumps(msg)
    print("payload:{}".format(len(payload)))
    bytes_sent = s.sendto(payload,addr)
    time.sleep(INTERVAL)

接收方:

代码语言:javascript
复制
while True:
    if stop_receiving.get():
        break
    try:
        (payload,addr) = self.sock.recvfrom(32767)      
        (t,data) = pickle.loads(payload,encoding="bytes")       
        if stime >= self.frame_time.get():
            self.frames.put((t,data))
    except socket.timeout:          
        pass

在python3.4上使用泡菜格式3,如果我pickle.dumps一个整数的元组和2048个字节,我将得到2063个字节。

奇怪的是,在python2.7上使用泡菜格式2,我得到了5933字节,几乎是原来的3倍。

为什么有这么大的差别?

我是否应该制定一个协议并附加这些字节呢?我本来可以的,但当我找到泡菜后,我想它会有用的。

Python还说,可以使用压缩库来缩小大小,但我不知道额外的时间开销是否可以弥补。

谢谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-10-22 19:25:13

首先,通常情况下,主要的新版本的协议、库等都有重大的改进,这并不令人惊讶。否则,为什么有人会费心去做所有的工作来创建它们呢?

但你可能是在找细节。

在我们讨论其他内容之前,您的主要问题是,您不是在比较协议2和协议3,而是比较协议0和协议3。注意pickletools.dumps转储中的最后一行:highest protocol among opcodes = 2。如果您看到的是0而不是2,这意味着您使用的是协议0。协议0是为人类的可读性而设计的(嗯,至少没有像pickletools这样的库的人类可调试器),而不是为了紧凑。特别是,它将反斜杠-转义不可打印的ASCII字节,将其中的大部分扩展到4个字符。

那么,你为什么要得到0而不是2?因为,出于向后兼容性的原因,最高协议不是默认协议。缺省值在2.x中为0,在3.x中为3。请参阅2.73.4的文档。

如果您将代码更改为pickle.dumps(msg, protocol=pickle.HIGHEST_PROTOCOL) (或者只是protocol=-1),您将得到2和4,而不是0和3。出于下面解释的原因,2.x可能仍然比3.x大,但与您现在看到的规模不同。

如果您真的想要奇偶校验,如果协议-2的结果对您来说足够紧凑,您可能需要显式地使用protocol=2

如果您想显式地只使用2或3,就像您认为自己正在做的那样,没有直接的方法来编写它,但是protocol=min(3, pickle.HIGHEST_PROTOCOL)会这样做的。

pickletools模块(以及源代码中的注释,它是从文档中链接的)使得探索两者之间的区别变得更加容易。

让我们使用更短的字符串,以便更容易地查看:

代码语言:javascript
复制
>>> t = (1, string.ascii_lowercase.encode('ascii'))
>>> p2 = pickle.dumps(t, protocol=2)
>>> p3 = pickle.dumps(t, protocol=3)
>>> len(p2), len(p3)
78, 38

因此,明显的差别仍然存在。

现在,让我们看看泡菜里有什么。(您可能希望在自己的解释器中使用pickletools.dis(p2, annotate=1),但是由于大多数信息从屏幕边缘滚动,所以在这里…并不那么有用)

代码语言:javascript
复制
>>> pickletools.dis(p2)
    0: \x80 PROTO      2
    2: K    BININT1    1
    4: c    GLOBAL     '_codecs encode'
   20: q    BINPUT     0
   22: X    BINUNICODE 'abcdefghijklmnopqrstuvwxyz'
   53: q    BINPUT     1
   55: X    BINUNICODE 'latin1'
   66: q    BINPUT     2
   68: \x86 TUPLE2
   69: q    BINPUT     3
   71: R    REDUCE
   72: q    BINPUT     4
   74: \x86 TUPLE2
   75: q    BINPUT     5
   77: .    STOP
highest protocol among opcodes = 2

如您所见,协议2将bytes存储为Unicode字符串和编解码器。

代码语言:javascript
复制
>>> pickletools.dis(p3)
    0: \x80 PROTO      3
    2: K    BININT1    1
    4: C    SHORT_BINBYTES b'abcdefghijklmnopqrstuvwxyz'
   32: q    BINPUT     0
   34: \x86 TUPLE2
   35: q    BINPUT     1
   37: .    STOP
highest protocol among opcodes = 3

…但是,协议3使用协议2中不存在的新操作码将它们存储为bytes对象。

更详细的是:

BINUNICODE系列操作码接受一个Unicode字符串,并将其存储为长度前缀UTF-8。

BINBYTES系列操作码接受字节字符串并将其存储为长度前缀字节.

因为协议1和2没有BINBYTES,所以bytes实际上存储为对_codecs.encode的调用,参数为b.decode('latin-1')u'latin-1'。(为什么是拉丁语-1?可能是因为它是将每个字节映射到单个Unicode字符的最简单的编解码器。)

这增加了40字节的固定开销(这说明了我的p2p3之间的差异)。

更重要的是,对于您的情况,大多数非ASCII字节将结束为两个字节的UTF-8。对于随机字节,这大约是51%的总开销。

注意,在协议1和更高版本中有一个BINSTRING类型,它非常类似于BINBYTES,但是它被定义为在默认编码中存储字节,这一点几乎没有用。在2.x中,这并没有什么区别,因为您不会为了获得一个decode而使用它,但是我猜2.6+不会为了3.x的兼容性而使用它。

还有一个STRING类型,可以追溯到协议0,它在字符串上存储一个ASCII编码的repr。我认为它从来没有被用于协议1和更高。当然,这会将任何不可打印的ASCII字节炸成2或4字节的反斜杠转义。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26515272

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档