質問
標準の socket
モジュールを使用して、ソケットから行を読みたいとしましょう:
def read_line(s):
ret = ''
while True:
c = s.recv(1)
if c == '\n' or c == '':
break
else:
ret += c
return ret
s.recv(1)
で正確に何が起こりますか?毎回システムコールを発行しますか?とにかく、バッファリングを追加する必要があると思います:
ハードウェアおよびネットワークの現実と最適に一致させるには、 bufsize の値を2の比較的小さいべき乗、たとえば4096にする必要があります。
http://docs.python.org/library/socket。 html#socket.socket.recv
しかし、効率的でスレッドセーフなバッファリングを書くのは簡単ではないようです。 file.readline()
を使用するとどうなりますか?
# does this work well, is it efficiently buffered?
s.makefile().readline()
解決
recv()
呼び出しは、Cライブラリ関数を呼び出すことで直接処理されます。
ソケットがデータを持つのを待つことをブロックします。実際には、 recv()
システムコールブロックを許可します。
file.readline()
は効率的なバッファー付き実装です。ファイルを読み取るのはそれだけだと想定しているため、スレッドセーフではありません。 (たとえば、今後の入力をバッファリングすることによって。)
ファイルオブジェクトを使用している場合、正の引数で read()
が呼び出されるたびに、基になるコードは recv()
要求されたデータ量のみ、既にバッファリングされている場合を除きます。
次の場合、バッファリングされます:
-
readline()を呼び出して、バッファ全体を読み取ります
-
行の終わりはバッファの終わりの前でした
したがって、データをバッファに残します。それ以外の場合、バッファは通常、いっぱいになりません。
質問の目的は明確ではありません。読み取る前にデータが利用可能かどうかを確認する必要がある場合は、 select()
または s.setblocking(False)
でソケットを非ブロックモードに設定できます。次に、待機中のデータがない場合、読み取りはブロッキングではなく空を返します。
1つのファイルまたはソケットを複数のスレッドで読み込んでいますか?ソケットを読み取り、受信したアイテムを他のスレッドで処理するためのキューにフィードするために、単一のワーカーを配置します。
コンサルティングの提案 Pythonソケットモジュールソースおよび Cソースシステム呼び出しを行います。
他のヒント
パフォーマンスに関心があり、ソケットを完全に制御する場合 (たとえば、ライブラリに渡していません)、実装を試してください Pythonでの独自のバッファリング-Python string.findおよびstring.splitなど 驚くほど高速になります。
def linesplit(socket):
buffer = socket.recv(4096)
buffering = True
while buffering:
if "\n" in buffer:
(line, buffer) = buffer.split("\n", 1)
yield line + "\n"
else:
more = socket.recv(4096)
if not more:
buffering = False
else:
buffer += more
if buffer:
yield buffer
ペイロードが複数の行で構成されると予想される場合 大きすぎず、かなり高速に実行されるはずです。 機能の層が多すぎないようにします 不必要に呼び出します。知りたいと思う これがfile.readline()またはsocket.recv(1)を使用してどのように比較されるか。
def buffered_readlines(pull_next_chunk, buf_size=4096):
"""
pull_next_chunk is callable that should accept one positional argument max_len,
i.e. socket.recv or file().read and returns string of up to max_len long or
empty one when nothing left to read.
>>> for line in buffered_readlines(socket.recv, 16384):
... print line
...
>>> # the following code won't read whole file into memory
... # before splitting it into lines like .readlines method
... # of file does. Also it won't block until FIFO-file is closed
...
>>> for line in buffered_readlines(open('huge_file').read):
... # process it on per-line basis
...
>>>
"""
chunks = []
while True:
chunk = pull_next_chunk(buf_size)
if not chunk:
if chunks:
yield ''.join(chunks)
break
if not '\n' in chunk:
chunks.append(chunk)
continue
chunk = chunk.split('\n')
if chunks:
yield ''.join(chunks + [chunk[0]])
else:
yield chunk[0]
for line in chunk[1:-1]:
yield line
if chunk[-1]:
chunks = [chunk[-1]]
else:
chunks = []