Python:'取消屏蔽'长的XOR数据字符串

时间:2012-11-30 11:22:52

标签: python performance websocket xor

作为WebSocket spec的一部分,所有客户端发送的帧必须使用4字节掩码屏蔽帧的有效负载部分。在C ++中,这非常简单:

for (size_t i = 0; i < length; i++) {
    data[i] ^= mask[i % 4];
}

可悲的是,Python字符串是不可变的,我宁愿避免这样做,因为不断复制和重新创建字符串缓冲区:

frame = ''
for i in range(0, length-1):
    frame += chr(ord(oldFrame[i]) ^ ord(mask[i % 4]))

经过一些研究,我找到了这个:

m = itertools.cycle(mask)
frame = ''.join(chr(ord(x) ^ ord(y)) for (x,y) in itertools.izip(oldFrame, m))

在CPython中,将揭露所需的时间减半。在PyPy中,对于一个16MB的屏蔽字符串,这很容易增加到1.5GB的RAM使用量,之后它开始交换,我必须杀死它。 CPython为这个16MB字符串使用'仅'150 MB RAM(并且需要20秒),但这仍然很糟糕。相比之下,我的C ++基准测试在0.05秒内完成,没有内存开销。

当然,一旦软件进入生产模式(所有传入的数据上限为10KB),这些都是极端情况,但我真的希望在这个基准测试中取得好成绩。

有什么想法吗?唯一的要求是:CPython和PyPy都快,内存使用率低。不必保留原始字符串。

某些想要试验的测试代码:

import os, time

frame = os.urandom(16 << 20)
mask = os.urandom(4)

def unmask(oldFrame, mask):
    # Do your magic
    return newFrame

for i in range(0, 3): # Run several times, to help PyPy's JIT compiler
    startTime = time.time()
    f = unmask(frame, mask)
    endTime = time.time()
    print 'This run took %.3f seconds' % (endTime - startTime)

2 个答案:

答案 0 :(得分:2)

改为使用bytearray 可变。

frame = bytearray(frame)
for i in range(len(mask)):
    frame[i] ^= mask[i]

答案 1 :(得分:0)

您可能还会考虑numpy,它允许您将操作卸载到numpy中的高效C代码。当numpy不可用时,这是method that websockify uses,当使用支持可变字节数组的数组模块时,它会回退到较慢的方法。