内存高效的 Python 批处理

Posted

技术标签:

【中文标题】内存高效的 Python 批处理【英文标题】:Memory efficient Python batch processing 【发布时间】:2012-11-04 01:36:29 【问题描述】:

问题

我编写了一个小型 python 批处理器,它加载二进制数据、执行 numpy 操作并存储结果。它消耗的内存比它应该消耗的多得多。我查看了类似的stack-overflow discussions 并想寻求进一步的建议。

背景

我将光谱数据转换为 rgb。光谱数据存储在带线交错 (BIL) 图像文件中。这就是我逐行读取和处理数据的原因。我使用Spectral Python Library 读取数据,它返回一个numpy 数组。 hyp 是一个大型光谱文件的描述符:hyp.ncols=1600, hyp.nrows=3430, hyp.nbands=160

代码

import spectral
import numpy as np
import scipy

class CIE_converter (object):
   def __init__(self, cie):
       self.cie = cie

    def interpolateBand_to_cie_range(self, hyp, hyp_line):
       interp = scipy.interpolate.interp1d(hyp.bands.centers,hyp_line, kind='cubic',bounds_error=False, fill_value=0)
       return interp(self.cie[:,0])

    #@profile
    def spectrum2xyz(self, hyp):
       out = np.zeros((hyp.ncols,hyp.nrows,3))
       spec_line = hyp.read_subregion((0,1), (0,hyp.ncols)).squeeze()
       spec_line_int = self.interpolateBand_to_cie_range(hyp, spec_line)
       for ii in xrange(hyp.nrows):
          spec_line = hyp.read_subregion((ii,ii+1), (0,hyp.ncols)).squeeze()
          spec_line_int = self.interpolateBand_to_cie_range(hyp,spec_line)
          out[:,ii,:] = np.dot(spec_line_int,self.cie[:,1:4])
       return out

内存消耗

所有大数据都在循环外初始化。我天真的解释是内存消耗不应该增加(我使用了太多的 Matlab 吗?)有人可以解释一下增加因子 10 吗?这不是线性的,因为 hyp.nrows = 3430。 有什么建议可以改善内存管理吗?

  Line #    Mem usage    Increment   Line Contents
  ================================================
  76                                 @profile
  77     60.53 MB      0.00 MB       def spectrum2xyz(self, hyp):
  78    186.14 MB    125.61 MB           out = np.zeros((hyp.ncols,hyp.nrows,3))
  79    186.64 MB      0.50 MB           spec_line = hyp.read_subregion((0,1), (0,hyp.ncols)).squeeze()
  80    199.50 MB     12.86 MB           spec_line_int = self.interpolateBand_to_cie_range(hyp, spec_line)
  81                             
  82   2253.93 MB   2054.43 MB           for ii in xrange(hyp.nrows):
  83   2254.41 MB      0.49 MB               spec_line = hyp.read_subregion((ii,ii+1), (0,hyp.ncols)).squeeze()
  84   2255.64 MB      1.22 MB               spec_line_int = self.interpolateBand_to_cie_range(hyp, spec_line)
  85   2235.08 MB    -20.55 MB               out[:,ii,:] = np.dot(spec_line_int,self.cie[:,1:4])
  86   2235.08 MB      0.00 MB           return out

注释

我将 range 替换为 xrange 并没有大幅改进。我知道三次插值并不是最快的,但这与 CPU 消耗无关。

【问题讨论】:

spec_line_int 的形状和数据类型是什么? 您使用的是什么操作系统?在 Linux 上,您需要知道报告的内存是否包含 buffers and cache,否则这些数字可能会产生误导。 您可以通过在 numpy 函数(例如np.dot(spec_line_int, self.cie[:,1:4], out=out[:,ii,:]))中使用输出参数来节省一些 CPU 时间并减少堆碎片,以避免临时 numpy 数组的分配/释放。 (我真的不相信它会解决你的问题,但谁知道呢?) 根据文档spectral 库在真正访问之前不会读取数据:spectralpython.sourceforge.net/…。这可以解释为什么在处理数据时内存消耗会增加。 【参考方案1】:

感谢 cmets。他们都帮助我改善了一点内存消耗。 但最终我弄清楚了内存消耗的主要原因是什么:

SpectralPython 图像包含一个 Numpy Memmap 对象。这与高光谱数据立方体的数据结构具有相同的格式。 (如果是 BIL 格式(nrows、nbands、ncols)) 调用时:

spec_line = hyp.read_subregion((ii,ii+1), (0,hyp.ncols)).squeeze()

图像不仅作为numpy数组返回值返回,而且还缓存在hyp.memmap中。第二次调用会更快,但在我的情况下,内存只会增加,直到操作系统抱怨。由于 memmap 实际上是一个很好的实现,我将在以后的工作中直接利用它。

【讨论】:

从spectral 0.15.0 版开始,所有文件读取方法(例如您问题中的read_subregion)都接受可选的use_memmap 参数,可以将其设置为False 以避免使用内存映射接口。这将导致该方法改为直接读取文件,这通常会比 memmap 慢,但会减少内存消耗。

以上是关于内存高效的 Python 批处理的主要内容,如果未能解决你的问题,请参考以下文章

python中有哪些高效的数据结构来存储和处理大型数据集?

需要一个高效的内存缓存,每秒可以处理 4k 到 7k 的查找或写入

python高效处理大数据集1数据处理效率的迫切需求

Python如何高效处理复杂的嵌套字典

高效的 Python IPC [关闭]

深入理解JAVA虚拟机 高效并发