WebSocket仕様の一部として、クライアントから送信されるすべてのフレームには、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の文字列に「150MBの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)