如何有效地读取和写入太大而无法放入内存的文件?

时间:2015-08-23 01:15:44

标签: python numpy memory-management

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

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

这是我的代码:

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内存正在稳步攀升。在我看来,在整个过程结束时,整个输出矩阵将存储在内存中,这是我试图避免的。

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

如果没有,我做错了什么,我该如何解决?

感谢您的任何建议!

编辑:我刚刚意识到htop报告的系统内存占用总量为12G,所以它似乎正在工作......那里的任何人都可以启发我吗? RES现在是111G ......

EDIT2:memmap是从1D数组创建的,该数组由很多很多长小数组成,非常接近0,形状符合所需的尺寸。然后,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]])

2 个答案:

答案 0 :(得分:8)

就内存使用而言,目前您正在做的事情没有什么特别的错误。 Memmap数组在OS级别处理 - 要写入的数据通常保存在临时缓冲区中,并且只在OS认为必要时才提交到磁盘。在刷新写缓冲区之前,您的操作系统绝不允许您耗尽物理内存。

我建议不要在每次迭代时调用flush,因为这会让你的操作系统决定何时写入磁盘以达到最大效率。目前你只是一次编写单独的浮动值。

就IO和CPU效率而言,一次单行操作几乎肯定不是最理想的。对于大的,连续的数据块,读取和写入通常更快,如果使用向量化可以一次处理多行,则计算速度可能会快得多。一般的经验法则是处理数组中适合内存的大块(包括计算过程中创建的任何中间数组)。

Here's an example显示通过在适当大小的块中处理它们可以加快memmapped阵列上的操作速度。

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

同样的一般建议适用于使用HDF5而不是memmaps,但请记住,使用HDF5时,您必须自己处理所有内存管理。

答案 1 :(得分:7)

内存映射正如名称所示:将(虚拟)磁盘扇区映射到内存页面。内存由操作系统按需管理。如果有足够的内存,系统会将部分文件保留在内存中,可能会填满整个内存,如果没有足够的内存,系统可能会丢弃从文件读取的页面,或者可能将它们交换到交换空间。通常,您可以依赖操作系统尽可能高效。