2012-11-30 23 views
0

作为WebSocket spec的一部分,所有客户端发送的帧必须有使用4字节掩码来掩蔽帧的有效载荷部分。在C++中,这将是非常容易的:Python:'取消屏蔽'长的XOR'd数据串

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

使用bytearray代替,这是可变

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

哇,我想知道我是如何错过的。谢谢! –

0

您也可以考虑numpy的,这将让你在卸载的numpy的操作有效的C代码。这是method that websockify uses,如果numpy不可用,并且使用支持可变字节数组的数组模块,则会回退到较慢的方法。