Rui*_*lho 3 python sockets streaming pickle
流媒体端不断发送 2048 字节的声音样本以及作为整数的时间,一起发送到使用 pickle.dumps 进行腌制的元组中,然后将其以 UDP 数据包发送到接收器,然后接收器将其解开并缓冲它然后播放声音样本。
使用 python 3 一切都很好,接收器上的比特/秒速度是预期的。
当我在python 2.7中运行streamer时,速度更快了!我觉得 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)
Run Code Online (Sandbox Code Playgroud)
接收端:
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
Run Code Online (Sandbox Code Playgroud)
在使用 pickle format 3 的 python 3.4 上,如果我 pickle.dumps 一个整数和 2048 字节的元组,我会得到 2063 字节。
奇怪的是,在使用 pickle format 2 的 python 2.7 上,我得到了 5933 个字节,几乎是原来的 3 倍。
为什么这个差别这么大呢?
我应该制定一个协议并附加这些字节吗?我本来可以的,但在我找到泡菜之后,我相信它会起作用。
Python 文档还说可以使用压缩库来减小大小,但我不知道额外的时间开销是否可以补偿。
谢谢。
首先,作为一般规则,协议、库等的主要新版本有重大改进并不奇怪。否则,为什么会有人费尽心思去完成所有工作来创建它们呢?
\n\n但您可能正在寻找细节。
\n\n在我们讨论其他内容之前,您的大问题是您没有比较协议 2 和协议 3,而是比较协议 0 和协议 3。请注意pickletools.dumps下面转储中的最后一行:highest protocol among opcodes = 2。如果您看到0而不是2There,则意味着您正在使用协议 0。协议 0 是为人类可读性而设计的(嗯,至少是在没有像 之类的库的情况下的人类可调试性pickletools),而不是为了紧凑性。特别是,它将反斜杠转义不可打印的 ASCII 字节,将其中的大部分扩展为 4 个字符。
那么,为什么你得到 0 而不是 2 呢?因为,出于向后兼容性的原因,最高协议不是默认协议。在 2.x 中默认值为 0,在 3.x 中默认值为 3。请参阅2.7和3.4的文档.
\n\n如果您将代码更改为pickle.dumps(msg, protocol=pickle.HIGHEST_PROTOCOL)(或只是protocol=-1),您将得到 2 和 4,而不是 0 和 3。 2.x 可能仍会大于 3.x,原因如下所述,但远不及 3.x与您现在看到的规模相同。
如果您确实想要奇偶校验,如果协议 2 结果对您来说足够紧凑,您可能需要显式使用protocol=2.
如果你想明确地只使用 2 或 3,正如你所认为的那样,没有直接的方法来编写它,但protocol=min(3, pickle.HIGHEST_PROTOCOL)会这样做。
该pickletools模块(以及源代码中的注释,从文档链接)可以轻松探索差异。
让我们使用较短的字符串,以便于查看:
\n\n>>> t = (1, string.ascii_lowercase.encode(\'ascii\'))\n>>> p2 = pickle.dumps(t, protocol=2)\n>>> p3 = pickle.dumps(t, protocol=3)\n>>> len(p2), len(p3)\n78, 38\nRun Code Online (Sandbox Code Playgroud)\n\n所以,明显的区别仍然存在。
\n\n现在,让我们看看泡菜里有什么。(您可能想pickletools.dis(p2, annotate=1)在自己的解释器中使用,但由于大多数信息会滚动到屏幕边缘,因此这里没那么有用\xe2\x80\xa6)
>>> pickletools.dis(p2)\n 0: \\x80 PROTO 2\n 2: K BININT1 1\n 4: c GLOBAL \'_codecs encode\'\n 20: q BINPUT 0\n 22: X BINUNICODE \'abcdefghijklmnopqrstuvwxyz\'\n 53: q BINPUT 1\n 55: X BINUNICODE \'latin1\'\n 66: q BINPUT 2\n 68: \\x86 TUPLE2\n 69: q BINPUT 3\n 71: R REDUCE\n 72: q BINPUT 4\n 74: \\x86 TUPLE2\n 75: q BINPUT 5\n 77: . STOP\nhighest protocol among opcodes = 2\nRun Code Online (Sandbox Code Playgroud)\n\n如您所见,协议 2 存储bytes为 Unicode 字符串加编解码器。
>>> pickletools.dis(p3)\n 0: \\x80 PROTO 3\n 2: K BININT1 1\n 4: C SHORT_BINBYTES b\'abcdefghijklmnopqrstuvwxyz\'\n 32: q BINPUT 0\n 34: \\x86 TUPLE2\n 35: q BINPUT 1\n 37: . STOP\nhighest protocol among opcodes = 3\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6 但协议 3bytes使用协议 2 中不存在的新操作码将它们存储为对象。
更详细地说:
\n\n操作码系列BINUNICODE采用 Unicode 字符串并将其存储为长度前缀的 UTF-8。
操作码系列BINBYTES采用字节字符串并将其存储为长度前缀字节。
因为协议 1 和 2 没有BINBYTES,bytes实际上被存储为对和_codecs.encode的结果的调用作为参数。(为什么是 Latin-1?可能是因为它是将每个字节映射到单个 Unicode 字符的最简单的编解码器。)b.decode(\'latin-1\')u\'latin-1\'
这增加了 40 个字节的固定开销(这说明了我的p2和p3).
更重要的是,对于您的情况,大多数非 ASCII 字节最终将成为 UTF-8 的两个字节。对于随机字节,这大约是总开销的 51%。
\n\n请注意,协议 1 及更高版本中有一种BINSTRING类型,它与 非常相似BINBYTES,但它被定义为以默认编码存储字节,这几乎没有用处。在 2.x 中,这并不会真正产生影响,因为decode无论如何你都不会去获得一个str,但我的猜测是 2.6+ 不会将它用于 3.x 兼容性。
还有一种STRING类型可以追溯到协议 0,它repr在字符串上存储 ASCII 编码。我认为它从未在协议 1 及更高版本中使用过。这当然会将任何不可打印的 ASCII 字节放大为 2 或 4 字节反斜杠转义。