1. 程式人生 > 程式設計 >Python如何傳送與接收大型陣列

Python如何傳送與接收大型陣列

問題

你要通過網路連線傳送和接受連續資料的大型陣列,並儘量減少資料的複製操作。

解決方案

下面的函式利用 memoryviews 來發送和接受大陣列:

# 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.])
>>> 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相關函式, 比如 socket.send() send.recv_into() 。 在內部,這些方法能夠直接操作這個記憶體區域。例如,sock.send() 直接從記憶體中發生資料而不需要複製。 send.recv_into() 使用這個記憶體區域作為接受操作的輸入緩衝區。

剩下的一個難點就是socket函式可能只操作部分資料。 通常來講,我們得使用很多不同的 send() recv_into() 來傳輸整個陣列。 不用擔心,每次操作後,檢視會通過傳送或接受位元組數量被切割成新的檢視。 新的檢視同樣也是記憶體覆蓋層。因此,還是沒有任何的複製操作。

這裡有個問題就是接受者必須事先知道有多少資料要被髮送, 以便它能預分配一個數組或者確保它能將接受的資料放入一個已經存在的陣列中。 如果沒辦法知道的話,傳送者就得先將資料大小發送過來,然後再發送實際的陣列資料。

以上就是Python如何傳送與接收大型陣列的詳細內容,更多關於Python傳送接收大型陣列的資料請關注我們其它相關文章!