如何在lua中读取大文件(> 1GB)?
Posted
技术标签:
【中文标题】如何在lua中读取大文件(> 1GB)?【英文标题】:How to read large files (>1GB) in lua? 【发布时间】:2016-10-03 12:34:22 【问题描述】:我是 Lua 的新手(用于 Torch7 框架)。我有一个大小约为 1.4GB 的输入功能文件(文本文件)。简单的 io.open 函数在尝试打开此文件时会引发错误“内存不足”。在浏览用户组和文档时,我发现它可能是 Lua 限制。有解决方法吗?还是我在读取文件时做错了什么?
local function parse_file(path)
-- read file
local file = assert(io.open(path,"r"))
local content = file:read("*all")
file:close()
-- split on start/end tags.
local sections = string.split(content, start_tag)
for j=1,#sections do
sections[j] = string.split(sections[j],'\n')
-- remove the end_tag
table.remove(sections[j], #sections[j])
end
return sections
end
local train_data = parse_file(file_loc .. '/' .. train_file)
编辑:我试图读取的输入文件包含我想训练我的模型的图像特征。这个文件是有序的(start-tag ...contents...end-tagstart-tag ...等等...),所以如果我可以加载这些就可以了部分(开始标签到结束标签)一次一个。但是,我希望所有这些部分都加载到内存中。
【问题讨论】:
您确定在io.open
之后弹出“内存不足”吗?这似乎不对。但是,您可以分块读取文件吗?你真的需要内存中的hole文件吗注意file:read("*all")
中的*
在lua 5.3中已经过时了(我不知道torch使用哪个版本)
Torch 使用有内存限制的 LuaJIT。参见例如***.com/questions/35155444/….
@pschulz : 在执行local content = file:read("*all")
时弹出内存不足错误。
好吧,这似乎是合理的。请把这件事弄清楚。我当然知道你的意思是在调用 io 时。 阅读,但你永远不知道。但同样,您真的必须一次读取整个文件吗?
@pschulz :对于不清楚的问题,我深表歉意。在完成这项任务本身时,我正在深入了解 Lua。我试图读取的输入文件包含我想训练我的模型的图像特征。这个文件是有序的(事实证明,解决加载大文件问题的最简单方法是将 Torch 升级到 Lua5.2 或更高版本!正如 torch7-google-group 上的 Torch 开发人员所建议的那样。
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'
-- this function loads a file line by line to avoid having memory issues
local function load_file_to_tensor(path)
-- intialize tensor for the file
local file_tensor = torch.CharTensor()
-- Now we must determine the maximum size of the tensor in order to allocate it into memory.
-- This is necessary to allocate the tensor in one sweep, where columns correspond to letters and rows correspond to lines in the text file.
--[[ get number of rows/columns ]]
local file = io.open(path, 'r') -- open file
local max_line_size = 0
local number_of_lines = 0
for line in file:lines() do
-- get maximum line size
max_line_size = math.max(max_line_size, #line +1) -- the +1 is important to correctly fetch data
-- increment the number of lines counter
number_of_lines = number_of_lines +1
end
file:close() --close file
-- Now that we have the maximum size of the vector, we just have to allocat memory for it (as long there is enough memory in ram)
file_tensor = file_tensor:resize(number_of_lines, max_line_size):fill(0)
local f_data = file_tensor:data()
-- The only thing left to do is to fetch data into the tensor.
-- Lets open the file again and fill the tensor using ffi
local file = io.open(path, 'r') -- open file
for line in file:lines() do
-- copy data into the tensor line by line
ffi.copy(f_data, line)
f_data = f_data + max_line_size
end
file:close() --close file
return file_tensor
end
从这个张量中读取数据既简单又快捷。例如,如果您想读取文件中的第 10 行(将在张量的第 10 位),您可以简单地执行以下操作:
local line_string = ffi.string(file_tensor[10]:data()) -- this will convert into a string var
一个警告:这将占用更多的内存空间,并且对于一些行比另一行长得多的情况可能不是最佳的。但是,如果您没有内存问题,甚至可以忽略这一点,因为将张量从文件加载到内存时速度非常快,并且可能会在此过程中为您节省一些白发。
参考:https://groups.google.com/forum/#!topic/torch7/fi8a0RTPvDo
【讨论】:
【参考方案2】:我从来没有需要读取这么大的文件,但是如果您的内存不足,您可能需要逐行读取它。经过一些快速研究,我从 lua 网站上找到了这个:
buff = buff..line.."\n"
buff 是一个 50,020 字节的新字符串,而现在的旧字符串 > 垃圾。经过两个循环周期后,buff 是一个 50,040 字节的字符串,并且有两个旧字符串总共产生了超过 100 KB 的垃圾。因此,Lua 非常正确地决定现在是运行其垃圾收集器的好时机,因此它释放了这 100 KB。问题是这将每两个周期发生一次,因此 Lua 将在完成循环之前运行其垃圾收集器 2000 次。即使完成了所有这些工作,它的内存使用量也将是文件大小的三倍左右。更糟糕的是,每个连接都必须将整个字符串内容(50 KB 并且还在增长)复制到新字符串中。
因此,即使您逐行读取并每次都使用这样的连接,加载大文件似乎也会使用大量内存:
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 希望对您有所帮助。
【讨论】:
当心:旧代码。没有tinsert
或tremove
等。但这个想法仍然有效。
有点。将完整文件读入内存的最节省内存的方法仍然是file:read("*a")
。仅当您可以让 GC 收集以前的块时,读取较小的块才有意义。
@siffiejoe :所以使用file:read("*a")
读取文件是最好的选择吗?即一个人根本无法在 Lua 中读取大文件? ://
@Adam :感谢您的详细回答。但我认为这对我的情况没有帮助。从您收集此信息的页面上,它显示`要读取整个文件,您可以使用“*all”选项,它会立即读取它。但有时你没有这么简单的解决方案。那么,唯一的解决方案是为您的问题提供更有效的算法。所以即使是帖子也符合@sifijow 所说的内容?对此我还不是很清楚,如有错误请指正。
@NightFury13: file:read("*a")
是最好的选择如果你需要整个文件内容作为内存中的单个字符串。如果file:read("*a")
对您失败,则意味着您无法一次存储整个文件。我建议您对文件内容使用迭代器接口,以便您可以一个接一个地拉入一个元素。以上是关于如何在lua中读取大文件(> 1GB)?的主要内容,如果未能解决你的问题,请参考以下文章