我正在while循环中从UDP套接字读取数据。我需要最有效的方法
1)读取数据(*)(已经解决了,但是请多加注释)
2)定期将(操作的)数据转储到文件(**)中(问题)
我预计numpy的“ tostring”方法会出现瓶颈。让我们考虑以下(不完整的)代码:
import socket
import numpy
nbuf=4096
buf=numpy.zeros(nbuf,dtype=numpy.uint8) # i.e., an array of bytes
f=open('dump.data','w')
datasocket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# ETC.. (code missing here) .. the datasocket is, of course, non-blocking
while True:
gotsome=True
try:
N=datasocket.recv_into(buf) # no memory-allocation here .. (*)
except(socket.error):
# do nothing ..
gotsome=False
if (gotsome):
# the bytes in "buf" will be manipulated in various ways ..
# the following write is done frequently (not necessarily in each pass of the while loop):
f.write(buf[:N].tostring()) # (**) The question: what is the most efficient way to do this?
f.close()
据我所知,现在在(**):
1)buf [:N]为长度为N + 1的新数组对象分配内存,对吗? (也许不吧)
.. 在那之后:
2)buf [:N] .tostring()为新字符串分配内存,并将buf中的字节复制到该字符串中
似乎很多内存分配和交换。在以后的同一循环中,我将读取几个套接字并写入几个文件。
有没有办法告诉f.write直接从0到N个字节访问“ buf”的内存地址并将它们写入磁盘?
即,本着缓冲接口的精神做到这一点,并避免那两个额外的内存分配?
P. S. f.write(buf [:N] .tostring())等同于buf [:N] .tofile(f)
最佳答案
基本上,听起来您想使用数组的tofile
方法或直接使用ndarray.data
缓冲区对象。
对于您的确切用例,使用数组的data
缓冲区是最有效的,但是对于一般用途,您需要注意很多警告。我会详细说明。
但是,首先让我回答您的几个问题,并提供一些说明:
buf[:N]
为长度为N + 1的新数组对象分配内存,对吗?
这取决于您所说的“新数组对象”。无论涉及的数组大小如何,分配的额外内存都很少。
它确实为新的数组对象(几个字节)分配了内存,但没有为数组的数据分配额外的内存。相反,它创建一个共享原始数组数据缓冲区的“视图”。您对y = buf[:N]
所做的任何更改也会影响buf
。
buf [:N] .tostring()为新字符串分配内存,并将buf中的字节复制到该字符串中
对,那是正确的。
附带一提,您实际上可以采用相反的方式(从字符串到数组),而无需分配任何其他内存:
somestring = 'This could be a big string'
arr = np.frombuffer(buffer(somestring), dtype=np.uint8)
但是,由于python字符串是不可变的,因此
arr
将是只读的。有没有办法告诉f.write直接从0到N个字节访问“ buf”的内存地址并将它们写入磁盘?
是的
基本上,您需要:
f.write(buf[:N].data)
这非常有效,适用于任何类似文件的对象。在这种情况下,几乎绝对是您想要的。但是,有几个警告!
首先,请注意,
N
将位于数组的项目中,而不是直接以字节为单位。它们在示例代码中是等效的(由于dtype=np.int8
或任何其他8位数据类型)。如果您确实想写一些字节,可以
f.write(buf.data[:N])
...但是切片
arr.data
缓冲区将分配一个新的字符串,因此它的功能类似于buf[:N].tostring()
。无论如何,请注意对于大多数dtypes,执行f.write(buf[:N].tostring())
与执行f.write(buf.data[:N])
是不同的,但是两者都会分配一个新的字符串。接下来,numpy数组可以共享数据缓冲区。在您的示例案例中,您不必为此担心,但是通常,使用
somearr.data
可能会因此而引起意外。举个例子:
x = np.arange(10, dtype=np.uint8)
y = x[::2]
现在,
y
与x
共享相同的内存缓冲区,但是在内存中并不连续(请查看x.flags
与y.flags
)。相反,它引用x
的内存缓冲区中的所有其他项(将x.strides
与y.strides
比较)。如果尝试访问
y.data
,则会收到一条错误消息,告诉我们这不是内存中的连续数组,并且无法为其获取单段缓冲区:In [5]: y.data
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-54-364eeabf8187> in <module>()
----> 1 y.data
AttributeError: cannot get single-segment buffer for discontiguous array
这是numpy数组具有
tofile
方法的大部分原因(它也早于python的buffer
,但这是另一回事了)。tofile
会将数组中的数据写入文件,而无需分配额外的内存。但是,由于它是在C级实现的,因此仅适用于真正的file
对象,不适用于类似文件的对象(例如套接字,StringIO等)。例如:
buf[:N].tofile(f)
但是,这是在C级别实现的,仅适用于实际的文件对象,不适用于套接字,StringIO和其他类似文件的对象。
但是,这确实允许您使用任意数组索引。
buf[someslice].tofile(f)
将创建一个新视图(相同的内存缓冲区),并将其有效地写入磁盘。在您的确切情况下,它比切片
arr.data
缓冲区并将其直接写入磁盘要慢一些。如果您想使用数组索引(而不是字节数),则
ndarray.tofile
方法将比f.write(arr.tostring())
更有效。关于python - 将Python中的字节从Numpy数组复制到字符串或字节数组,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28341785/