流线端继续发送一个2048字节的声音样本,并将时间作为整数一起发送到一个用pickle.dumps进行腌制的元组中,然后发送一个UDP数据包给接收方,然后接收方对其进行解压缩、缓冲,然后播放声音样本。
使用python 3一切都很好,接收端的位/秒速度是预期的。
当我在python2.7中运行流光时,速度更快了!我坚韧的蟒蛇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在python3.4上使用泡菜格式3,如果我pickle.dumps一个整数的元组和2048个字节,我将得到2063个字节。
奇怪的是,在python2.7上使用泡菜格式2,我得到了5933字节,几乎是原来的3倍。
为什么有这么大的差别?
我是否应该制定一个协议并附加这些字节呢?我本来可以的,但当我找到泡菜后,我想它会有用的。
Python还说,可以使用压缩库来缩小大小,但我不知道额外的时间开销是否可以弥补。
谢谢。
发布于 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.7和3.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模块(以及源代码中的注释,它是从文档中链接的)使得探索两者之间的区别变得更加容易。
让我们使用更短的字符串,以便更容易地查看:
>>> 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),但是由于大多数信息从屏幕边缘滚动,所以在这里…并不那么有用)
>>> 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字符串和编解码器。
>>> 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字节的固定开销(这说明了我的p2和p3之间的差异)。
更重要的是,对于您的情况,大多数非ASCII字节将结束为两个字节的UTF-8。对于随机字节,这大约是51%的总开销。
注意,在协议1和更高版本中有一个BINSTRING类型,它非常类似于BINBYTES,但是它被定义为在默认编码中存储字节,这一点几乎没有用。在2.x中,这并没有什么区别,因为您不会为了获得一个decode而使用它,但是我猜2.6+不会为了3.x的兼容性而使用它。
还有一个STRING类型,可以追溯到协议0,它在字符串上存储一个ASCII编码的repr。我认为它从来没有被用于协议1和更高。当然,这会将任何不可打印的ASCII字节炸成2或4字节的反斜杠转义。
https://stackoverflow.com/questions/26515272
复制相似问题