2012-11-30 60 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不可用,並且使用支持可變字節數組的數組模塊,則會回退到較慢的方法。