流媒体方面一直发送一个2048字节的声音样本以及整数的时间,一起在使用pickle.dumps进行pickle的元组中,然后将UDP数据包发送到接收器,然后接收器将其解开,缓冲它,然后播放声音样本。
使用python 3一切都很好,预计接收器上的比特/秒速度。
当我在python 2.7中运行流光时,速度更快!我强硬的python 2在某种程度上更快。
然后我用wireshark检查了接收器收到的UDP数据包,它们比他们需要的要大。
流光方面:
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)
接收方:
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
在python 3.4上使用pickle格式3,如果我pickle.dumps一个整数和2048字节的元组,我得到2063字节。
奇怪的是,在使用pickle格式2的python 2.7上,我获得5933字节,几乎是3倍。
为什么这种差异如此之大?
我应该制作一个协议并添加这些字节吗?我可以拥有,但是在我发现泡菜之后,我认为它会起作用。
Python文档还说可以使用压缩库来减小大小,但我不知道额外的时间开销是否会补偿。
谢谢。答案 0 :(得分:4)
首先,作为一般规则,协议,库等的主要新版本具有重大改进并不令人惊讶。否则,为什么有人会费心去做所有的工作来创造它们呢?
但你可能正在寻找细节。
在我们进入其他任何事情之前,您的主要问题是不比较协议2和协议3,您正在比较协议0和协议3.请注意{中的最后一行{1}}转储如下:pickletools.dumps
。如果你在那里看到highest protocol among opcodes = 2
而不是0
,那就意味着你正在使用协议0.协议0是为了人类可读性而设计的(好吧,至少人工可调试性没有像2
这样的库) ,不是为了紧凑。特别是,它将反斜杠转义为非可打印的ASCII字节,将大部分字节扩展为4个字符。
那么,你为什么得到0而不是2?因为,出于向后兼容性原因,最高协议不是默认协议。 2.x中的默认值为0,3.x中的默认值为3。请参阅2.7和3.4的文档。
如果您将代码更改为pickletools
(或仅pickle.dumps(msg, protocol=pickle.HIGHEST_PROTOCOL)
),则会得到2和4而不是0和3. 2.x仍可能大于3。 x,由于下面解释的原因,但你现在看到的规模并不相同。
如果您真的想要奇偶校验,如果协议-2结果足够紧凑,您可能希望明确使用protocol=-1
。
如果您想明确地只使用2或3,就像您认为的那样,没有直接方式来编写它,但是protocol=2
会这样做。
pickletools
模块(以及源代码中的注释,从文档链接)可以轻松探索差异。
让我们使用更短的字符串,以便更容易查看:
protocol=min(3, pickle.HIGHEST_PROTOCOL)
所以,明显的区别仍然存在。
现在,让我们来看看泡菜中的含义。 (您可能希望在自己的解释器中使用>>> 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)
如您所见,协议2将>>> 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
存储为Unicode字符串加编解码器。
bytes
...但是协议3使用协议2中不存在的新操作码将它们存储为>>> 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
对象。
更详细:
bytes
系列操作码采用Unicode字符串并将其存储为长度为前缀的UTF-8。
BINUNICODE
系列操作码采用字节字符串并将其存储为长度为前缀的字节。
由于协议1和协议2没有BINBYTES
,BINBYTES
实际上存储为bytes
,_codecs.encode
和{{{ 1}}作为参数。 (为什么使用Latin-1?可能是因为它是将每个字节映射到单个Unicode字符的最简单的编解码器。)
这增加了40个字节的固定开销(这说明了我的b.decode('latin-1')
和u'latin-1'
之间的差异。)
更重要的是,对于您的情况,大多数非ASCII字节最终将是UTF-8的两个字节。对于随机字节,总开销约为51%。
请注意,协议1及更高版本中的 是p2
类型,与p3
非常相似,但它被定义为在默认情况下存储字节编码,这几乎没用。在2.x中,这不会产生任何影响,因为你无论如何都不会BINSTRING
获得BINBYTES
,但我的猜测是2.6+不使用它3.x兼容性。
还有decode
类型可追溯到协议0,协议0在字符串上存储ASCII编码的str
。我不认为它曾在协议1和更高版本中使用过。这当然会将任何非可打印的ASCII字节炸成2或4字节的反斜杠转义。