如何有效地读写内存无法容纳的大文件?



我正在尝试计算100,000个向量的余弦相似度,这些向量中的每个向量有200,000个维度。

从阅读其他问题,我知道memmap, PyTables和h5py是我处理这类数据的最佳选择,我目前正在使用两个memmap;一个用于读取向量,另一个用于存储余弦相似度矩阵。

下面是我的代码:
import numpy as np
import scipy.spatial.distance as dist
xdim = 200000
ydim = 100000
wmat = np.memmap('inputfile', dtype = 'd', mode = 'r', shape = (xdim,ydim))
dmat = np.memmap('outputfile', dtype = 'd', mode = 'readwrite', shape = (ydim,ydim))
for i in np.arange(ydim)):
    for j in np.arange(i+1,ydim):
        dmat[i,j] = dist.cosine(wmat[:,i],wmat[:,j])
        dmat.flush()

目前,htop报告我使用了224G的VIRT内存和91.2G的RES内存,并且还在稳步攀升。在我看来,在过程结束时,整个输出矩阵将存储在内存中,这是我试图避免的事情。

问题:这是memmap的正确使用吗?我是否以内存有效的方式写入输出文件(我的意思是只有输入和输出文件的必要部分,即dmat[i,j]wmat[:,i/j],存储在内存中)?

如果没有,我做错了什么,我如何解决这个问题?

谢谢你的任何建议!

编辑:我刚刚意识到htop报告的总系统内存使用量为12G,所以看起来它毕竟在工作…有人能指点我吗?RES现在是111G…

EDIT2: memmap是由一个由许多非常接近0的长小数组成的1D数组创建的,该数组的形状符合所需的尺寸。然后memmap看起来像这样:

memmap([[  9.83721223e-03,   4.42584107e-02,   9.85033578e-03, ...,
     -2.30691545e-07,  -1.65070799e-07,   5.99395837e-08],
   [  2.96711345e-04,  -3.84307391e-04,   4.92968462e-07, ...,
     -3.41317722e-08,   1.27959347e-09,   4.46846438e-08],
   [  1.64766260e-03,  -1.47337747e-05,   7.43660202e-07, ...,
      7.50395136e-08,  -2.51943163e-09,   1.25393555e-07],
   ..., 
   [ -1.88709000e-04,  -4.29454722e-06,   2.39720287e-08, ...,
     -1.53058717e-08,   4.48678211e-03,   2.48127260e-07],
   [ -3.34207882e-04,  -4.60275148e-05,   3.36992876e-07, ...,
     -2.30274532e-07,   2.51437794e-09,   1.25837564e-01],
   [  9.24923862e-04,  -1.59552854e-03,   2.68354822e-07, ...,
     -1.08862665e-05,   1.71283316e-07,   5.66851420e-01]])

就内存使用而言,您目前所做的并没有什么特别的错误。memmap数组是在操作系统级别处理的——要写的数据通常保存在一个临时缓冲区中,只有当操作系统认为有必要时才提交到磁盘。在刷新写缓冲区之前,你的操作系统不应该允许你用完物理内存。

我建议不要在每次迭代中调用flush,因为这违背了让操作系统决定何时写入磁盘以最大化效率的目的。现在你一次只能写一个浮点数

在IO和CPU效率方面,一次在单行上操作几乎肯定是次优的。对于大的、连续的数据块,读和写通常更快,同样,如果您可以使用向量化一次处理多行,您的计算可能会快得多。一般的经验法则是处理内存中适合的数组块(包括在计算过程中创建的任何中间数组)。

下面的示例显示了通过在适当大小的块中处理memmap数组可以提高多少操作速度。

另一个可以产生巨大差异的是输入和输出数组的内存布局。默认情况下,np.memmap为您提供了一个连续的(行为主)数组。因此,按列访问wmat将非常低效,因为您正在寻址磁盘上的非相邻位置。如果wmat在磁盘上是f连续的(列为主),或者按行访问它,情况会好得多。

同样的一般建议适用于使用HDF5而不是memmaps,尽管记住HDF5您将不得不自己处理所有的内存管理

内存映射顾名思义:(虚拟)磁盘扇区到内存页的映射。内存由操作系统按需管理。如果有足够的内存,系统将文件的一部分保留在内存中,可能会填满整个内存,如果剩余的内存不够,系统可能会丢弃从文件读取的页面,或者将它们交换到交换空间中。通常情况下,你可以依靠操作系统是尽可能高效的。

最新更新