Python 如何与NumPy ndarray共享HDF5数据集中的内存
我正在编写一个应用程序,用于从传感器传输数据,然后以各种方式处理数据。这些处理组件包括数据可视化、一些数字运算(线性代数),以及以HDF5格式将数据写入磁盘。理想情况下,这些组件中的每一个都是自己的模块,都在同一个Python进程中运行,因此IPC不是问题。这就引出了如何高效地存储流数据的问题 数据集非常大(~5Gb),因此我希望通过在需要访问的组件之间共享数据来最小化内存中的数据副本数量。如果所有组件都直接使用Python 如何与NumPy ndarray共享HDF5数据集中的内存,python,numpy,hdf5,h5py,Python,Numpy,Hdf5,H5py,我正在编写一个应用程序,用于从传感器传输数据,然后以各种方式处理数据。这些处理组件包括数据可视化、一些数字运算(线性代数),以及以HDF5格式将数据写入磁盘。理想情况下,这些组件中的每一个都是自己的模块,都在同一个Python进程中运行,因此IPC不是问题。这就引出了如何高效地存储流数据的问题 数据集非常大(~5Gb),因此我希望通过在需要访问的组件之间共享数据来最小化内存中的数据副本数量。如果所有组件都直接使用ndarrays,那么这应该很简单:将数据交给其中一个进程,然后使用ndarray.
ndarray
s,那么这应该很简单:将数据交给其中一个进程,然后使用ndarray.view()
给其他每个进程一个副本
但是,将数据写入磁盘的组件将数据存储在HDF5数据集中。它们可以在许多方面与ndarray
s互操作,但创建view()
似乎不像ndarray
s那样有效
用ndarray
s观察:
>>> source = np.zeros((10,))
>>> view = source.view()
>>> source[0] = 1
>>> view[0] == 1
True
>>> view.base is source
True
但是,这不适用于HDF5Dataset
s:
>>> import h5py
>>> file = h5py.File('source.h5', 'a')
>>> source_dset = file.create_dataset('source', (10,), dtype=np.float64)
>>> view_dset = source_dset.value.view()
>>> source_dset[0] = 1
>>> view_dset[0] == 1
False
>>> view_dset.base is source_dset.value
False
仅仅分配数据集.value本身,而不是它的视图,也不起作用
>>> view_dset = source_dset.value
>>> source_dset[0] = 2
>>> view_dset[0] == 2
False
>>> view_dset.base is source_dset.value
False
所以我的问题是:有没有办法让ndarray
与HDF5Dataset
共享内存,就像两个ndarray
可以共享内存一样?
我的猜测是,这不太可能奏效,可能是因为HDF5在内存中存储阵列的方式有些微妙。但这让我有点困惑,尤其是type(source\u dset.value)==numpy.ndarray
和Dataset.value.view()的OWNDATA
标志实际上是False
。谁拥有视图
正在解释的内存
版本详细信息:Python 3、NumPy版本1.9.1、h5py版本2.3.1、HDF5版本1.8.13、Linux
其他详细信息:HDF5文件已分块
编辑:
在进一步讨论这个问题之后,一个可能的解决方案似乎是给其他组件一个对HDF5Dataset
本身的引用。这似乎不会复制任何内存(至少根据top
),并且源数据集中的更改会反映在视图中
>>> import h5py
>>> file = h5py.File('source.h5', 'a')
>>> source = file.create_dataset('source', (10,), dtype=np.float64)
>>> class Container():
... def __init__(self, source_dset):
... self.dset = source_dset
...
>>> container = Containter(source)
>>> source[0] = 1
>>> container.dset[0] == 1
True
我对这个解决方案相当满意(只要节省了内存),但我仍然很好奇为什么上面的视图方法不起作用。简单的回答是,你不能在numpy
数组和h5py
数据集之间共享内存。虽然它们有类似的API(至少在索引方面是如此),但它们没有兼容的内存布局。事实上,除了某种缓存之外,数据集甚至不在内存中——它在文件中
首先,我不明白为什么需要将source.view()
与numpy
数组一起使用。是,当从数组中选择或重塑数组时,numpy
尝试返回一个视图,而不是一个副本。但是.view
的大多数(全部?)示例都涉及某种转换,例如使用dtype
。您能否仅使用.view()
指向代码或文档示例
我对h5py
没有太多经验,但它的文档中提到它在h5
文件对象周围提供了一个类似于ndarray的薄型包装器。您的数据集
不是ndarray
。例如,它缺少许多ndarray
方法,包括view
但是索引数据集
会返回一个ndarray
,例如视图\u数据集[:]
。.value
也是如此。其文档的第一部分(通过IPython
中的view\u dset.value???
):
x
、xdset
和x1
都是独立的-更改一个不会更改其他
关于时间,比较
timeit np.sum(x) # 11.7 µs
timeit np.sum(xdset) # 203 µs
timeit xdset.value # 173 µs
timeit np.sum(x1) # same as for x
数组的和
比数据集快得多。大部分额外的时间都涉及到从数据集创建数组。请注意,传递数据集对象会对性能造成巨大影响。此外,访问数据集不会一次将其全部加载到内存中,这允许您以最小的内存开销仅提取数据集的一个子集。此外,如果您的数据是分块的,它甚至可能在文件中不是连续的(甚至可能被压缩)。hdf5的主要功能之一是能够任意选择hyberslab,隐藏这些细节是h5py的主要功能之一。@tcaswell澄清数据确实是分块和压缩的,因此在文件中很可能不是连续的。所以我想你是对的,任何给定的块可能存在于内存中,也可能不存在于内存中,因此ndarray
必须复制数据,而不是引用数据。那它为什么不拥有自己的数据呢?另外,您考虑的性能影响是什么?将np.sum(data\u set)
与np.sum(data\u set[:])
.value
是数据集
的一个属性,该数组由一些getter
函数生成。因此,对.value
的一次调用可能不会产生与下一次调用相同的对象。预订h5py
,源代码指示索引操作使用文件中的数据填充新的ndarray
。我想这样做是为了防止任何抄袭,因为有很多读者,但只有一个作者。但你们的观点被很好地理解了,让NumPy处理查看/复制可能会更好。
x = np.arange(10)
xdset = file.create_dataset('x', data=x)
x1 = xdset[:]
timeit np.sum(x) # 11.7 µs
timeit np.sum(xdset) # 203 µs
timeit xdset.value # 173 µs
timeit np.sum(x1) # same as for x