如何在lua中读取大文件(>1GB)?

我是Lua的新手(在Torch7框架中使用它)。我有一个约1.4GB大小(文本文件)的输入特征文件。简单的io.open函数在尝试打开此文件时会抛出“内存不足”的错误。在浏览用户组和文档时,我发现这可能是Lua的限制。是否有解决方法?或者我在读取文件时做错了什么?

local function parse_file(path)
    -- 读取文件
    local file = assert(io.open(path,"r"))
    local content = file:read("*all")
    file:close()

    -- 按开始/结束标记分割。
    local sections = string.split(content, start_tag)
    for j=1,#sections do
        sections[j] = string.split(sections[j],'\n')
        -- 移除结束标记
        table.remove(sections[j], #sections[j])
    end
    return sections
end

local train_data = parse_file(file_loc .. '/' .. train_file)

编辑:我正在尝试读取的输入文件包含我想要训练模型的图像特征。这个文件是有序的({start-tag}...内容...{end-tag}{start-tag}...等等...),所以如果可以逐个加载这些节(从开始标记到结束标记),那就没问题了。然而,我想要将所有这些节加载到内存中。

点赞
用户6336645
用户6336645

我从未需要阅读如此大的文件,但如果你的内存不够,你可能需要逐行阅读它。在进行了一些快速研究后,我从Lua网站找到了这个:

buff = buff..line.."\n"

buff是一个包含50,020字节的新字符串,旧字符串现在被丢弃。经过两个循环周期,buff是一个包含50,040字节的字符串,有两个旧字符串,总共超过100 Kbytes的垃圾。因此,Lua决定非常正确地运行垃圾回收器,释放这100 Kbytes。问题是这将在每两个周期发生一次,因此Lua会在完成循环之前运行它的垃圾回收器两千次。即使进行了所有这些工作,它的内存使用量也将是文件大小的三倍左右。更糟糕的是,每次连接都必须将整个字符串内容(50 Kbytes和增长)复制到新字符串中。

因此,即使你按照以下方式逐行阅读并每次使用连接,加载大文件似乎会使用大量内存:

local buff = ""
while 1 do
    local line = read()
    if line == nil then break end
    buff = buff..line.."\n"
end

他们随后提出了一种更加节省内存的处理方式:

  function newBuffer ()
      return {n=0}     -- 'n' counts number of elements in the stack
  end

  function addString (stack, s)
    table.insert(stack, s)       -- push 's' into the top of the stack
    for i=stack.n-1, 1, -1 do
      if string.len(stack[i]) > string.len(stack[i+1]) then break end
      stack[i] = stack[i]..table.remove(stack)
    end
  end

  function toString (stack)
    for i=stack.n-1, 1, -1 do
      stack[i] = stack[i]..table.remove(stack)
    end
    return stack[1]
  end

比以前使用更少的内存。所有信息都来自于:http://www.lua.org/notes/ltn009.html

希望有所帮助。

2016-06-03 13:09:25
用户3810057
用户3810057

原来,解决加载大文件的最简单方法是将 Torch 升级到 Lua5.2 或更高版本!正如 Torch 在 torch7-google-group 论坛上开发者所建议的那样。

cd ~/torch
./clean.sh
TORCH_LUA_VERSION=LUA52 ./install.sh

从 5.2 版本开始,内存限制不存在!我已经测试过了,它可以正常工作!

参考:https://groups.google.com/forum/#!topic/torch7/fi8a0RTPvDo


另一个可能的解决方案(更优雅,类似于 @Adam 在他的答案中提出的建议)是逐行读取文件并使用张量或 tds 存储数据,因为它使用 Luajit 之外的内存。以下是一个代码示例,感谢 Vislab。

local ffi = require 'ffi'
-- 此函数逐行加载文件以避免出现内存问题
local function load_file_to_tensor(path)
  -- 为文件初始化张量
  local file_tensor = torch.CharTensor()

  -- 现在我们必须确定张量的最大大小,以便将其分配到内存中。
  -- 这必须在一次扫描中分配张量,其中列对应于字母,行对应于文本文件中的行。

  --[[ 获取行数和列数 ]]
  local file = io.open(path, 'r') -- 打开文件
  local max_line_size = 0
  local number_of_lines = 0
  for line in file:lines() do
    -- 获取最大的行大小
    max_line_size = math.max(max_line_size, #line +1) -- 加上 1 非常重要,以便正确获取数据

    -- 增加行数计数器
    number_of_lines = number_of_lines +1
  end
  file:close() --关闭文件

  -- 现在我们已经获得了向量的最大大小,只需要为它分配内存即可(只要内存够大)
  file_tensor = file_tensor:resize(number_of_lines, max_line_size):fill(0)
  local f_data = file_tensor:data()

  -- 唯一剩下的事情就是从文件中获取数据到张量中。
  -- 让我们再次打开文件并使用 ffi 来填充张量
  local file = io.open(path, 'r') -- 打开文件
  for line in file:lines() do
    -- 逐行将数据复制到张量中
    ffi.copy(f_data, line)
    f_data = f_data + max_line_size
  end
  file:close() --关闭文件

  return file_tensor
end

从这个张量中读取数据很简单快捷。例如,如果您要读取文件中的第十行(将在张量的第十个位置),则可以简单地执行以下操作:

local line_string = ffi.string(file_tensor[10]:data()) -- 这将转换为字符串变量

警告:这将占用更多的内存空间,对于一些行比其他行长得多的情况可能不是最优选。但是,如果您没有内存问题,这甚至可以被忽略,因为将张量从文件加载到内存中时速度非常快,并且可能会在过程中节省一些头发。

参考:https://groups.google.com/forum/#!topic/torch7/fi8a0RTPvDo

2016-06-08 11:09:59