Pythonは大型配列をどのように送信して受信しますか?


問題
ネットワーク接続を通じて連続データの大行列を送信し、受信し、データのコピーをできるだけ減らすようにします。
ソリューション
以下の関数は、メモリビューを利用して、大きな配列を送信して受け入れます。

# zerocopy.py

def send_from(arr, dest):
  view = memoryview(arr).cast('B')
  while len(view):
    nsent = dest.send(view)
    view = view[nsent:]

def recv_into(arr, source):
  view = memoryview(arr).cast('B')
  while len(view):
    nrecv = source.recv_into(view)
    view = view[nrecv:]
テストプログラムのために、まずsocketで接続されたサーバとクライアントプログラムを作成します。

>>> from socket import *
>>> s = socket(AF_INET, SOCK_STREAM)
>>> s.bind(('', 25000))
>>> s.listen(1)
>>> c,a = s.accept()
>>>
クライアント(別の解釈器):

>>> from socket import *
>>> c = socket(AF_INET, SOCK_STREAM)
>>> c.connect(('localhost', 25000))
>>>
今回の目標は接続を通じて超大型の配列を転送することです。この場合、配列はarrayモジュールまたはnumpyモジュールで作成できます。

# Server
>>> import numpy
>>> a = numpy.arange(0.0, 50000000.0)
>>> send_from(a, c)
>>>

# Client
>>> import numpy
>>> a = numpy.zeros(shape=50000000, dtype=float)
>>> a[0:10]
array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
>>> recv_into(a, c)
>>> a[0:10]
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
>>>
討論する
データ密集型分散計算と平行計算プログラムでは、大量のデータを送信/受信するために自分でプログラムを書くことは一般的ではない。しかし、このようにしたいなら、低い層のネットワーク関数に使うために、あなたのデータを元のバイトに変換する必要があります。データを複数のブロックに切断する必要があるかもしれません。大部分のネットワーク関連の関数は、一度に送信または受信できないからです。
一つの方法は、あるメカニズムを使用してデータを逐次化することであり、それをバイト文字列に変換することができる。しかし、これは最終的にデータのコピーを作成します。これらをこまごまと作っても、コードは最終的に大量の小型コピーがあります。
このセクションでは、メモリビューを使っていくつかの魔法操作を示しています。本質的には、メモリビューはすでに存在している配列のカバー層です。それだけでなく、メモリビューは異なるタイプに変換してデータを表現することができます。これは次の文の目的です。

view = memoryview(arr).cast('B')
配列arrを受け入れ、記号なしのバイトのメモリビューに変換します。このビューは、socket.send() またはsend.recv_into() などのsocket関連関数に伝達され得る。内部では、これらの方法はこのメモリ領域を直接操作することができる。例えば、sock.send() は、コピーを必要とせずにメモリから直接データを発生する。send.recv_into() は、このメモリ領域を操作を受け付ける入力バッファとして使用する。
残りの難点はsocket関数が部分データだけを操作する可能性があります。一般的には、私たちは多くの異なるsend() recv_into() を使用して、配列全体を転送します。心配しないでください。操作毎に、送信または受信バイト数によって、新しいビューにカットされます。新しいビューもメモリの上書きです。したがって、まだ何のコピーもありません。
ここで問題があるのは、受信者が事前にどれぐらいのデータが送信されるかを知っていなければなりません。それは、事前に配列を割り当てられたり、受け入れられたデータをすでに存在している配列に入れることができるかを確認することができます。もし分からないなら、送信者はデータサイズを先に送って、実際の配列データを送ります。
以上はPythonがどのように大型配列の詳細を送信して受信しますか?Pythonが大規模配列を受信することについての資料は他の関連記事に注目してください。