標準の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つのファイルまたはソケットを読み取っていますか?私は1人のワーカーにソケットを読み取らせ、他のスレッドで処理するために受信したアイテムをキューに入れました。
コンサルティングを提案 Python Socket Module source および システムコールを作成する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 = []