概述是我正在编写代码以了解我的问题空间,并且我遇到了'PANIC:调用Lua API(内存不足)时出现无保护错误'错误。我正在寻找绕过这个限制的方法。
环境底线是Torch,一个在LuaJIT上运行的科学计算框架,LuaJIT在Lua上运行。我需要Torch,因为我最终想在GPU上使用神经网络解决我的问题,但为了实现这一目标,我需要很好地代表问题来提供给网络。我(卡住)在Centos Linux上,我怀疑尝试从32位模式重建源代码(据报道将LuaJIT内存限制扩展到4gb)将是一个噩梦,如果它适用于所有的库。
问题空间本身可能不是特别相关,但总的来说,我有点的数据文件,我计算这些距离之间的距离然后bin(即直方图)这些距离试图找出最有用的范围。方便的是,我可以创建复杂的Lua表,其中包含各种箱子和torch.save(),这些都是数不清的,然后捡起来并检查不同的标准化等等 - 所以经过一个月的游戏,我发现这是真的很容易和强大。
我可以让它工作查看最多3个距离,每个15个区(15x15x15加上开销),但这只是通过添加显式的garbagecollection()调用并使用fork()/ wait()为每个数据文件,以便外部如果一个数据文件(几千个)仍然会破坏内存限制并使孩子崩溃,那么循环将继续运行。当每个成功的子进程现在必须读取,修改和写入当前的bin计数集时,这会变得非常痛苦 - 我最大的文件目前是36mb。我想要更大(更多的箱子),并且真的更愿意只持有我无法访问的15演出RAM中的计数。
所以,这里有一些我想过的路径;如果你能确认/否认他们中的任何一个会/不会让我超出1gb边界,或者只是提高我的效率,请发表评论。如果您可以提出我没想过的其他方法,请发表评论。
我错过了一个触发Lua进程的方法,我可以从中读取任意表格吗?毫无疑问,我可以将我的问题分解成更小的部分,但是从stdio解析返回表(从系统调用到另一个Lua脚本)似乎容易出错,写入/读取小型中间文件将是很多磁盘i / o。
我错过了一个存储和访问表高内存模块吗?这似乎是我真正想要的,但还没有找到它
FFI C数据结构可以放在1gb之外吗?看起来似乎并非如此,但我当然缺乏对造成限制的原因的全面理解。我怀疑这会让我比普通的Lua表更能提高效率,因为这些表已经超越原型设计了吗? (除非我为每次更改做了一堆编码)
当然我可以通过在C中写一个扩展来解决(Torch似乎支持超出限制的网络),但我的简短调查结果显示对'lightuserdata'指针的引用 - 这是否意味着一个更正常的扩展也不会超出1GB?对于应该进行原型制作的情况,这似乎也有很大的开发成本。
我很了解C,所以走FFI或扩展路线并没有打扰我 - 但我从经验中知道,以这种方式封装算法既可以非常优雅,又可以通过两个地方来隐藏错误。处理包含堆栈中表格中的表格的数据结构似乎也不是很好。在我做出这些努力之前,我想确定最终结果真的能解决我的问题。
感谢阅读长篇文章。
答案 0 :(得分:9)
只有LuaJIT本身分配的对象仅限于前2GB的内存。这意味着表,字符串,完整用户数据(即不是lightuserdata)和分配有ffi.new
的FFI对象将计入限制,但分配有malloc
,mmap
等的对象是不受此限制(无论是否由C模块或FFI调用)。
使用malloc
分配结构的示例:
ffi.cdef[[
typedef struct { int bar; } foo;
void* malloc(size_t);
void free(void*);
]]
local foo_t = ffi.typeof("foo")
local foo_p = ffi.typeof("foo*")
function alloc_foo()
local obj = ffi.C.malloc(ffi.sizeof(foo_t))
return ffi.cast(foo_p, obj)
end
function free_foo(obj)
ffi.C.free(obj)
end
在LuaJIT 3.0 IIRC中实施的新GC不会有这个限制,但我最近没有听到任何关于它的发展的消息。
答案 1 :(得分:5)
以下是稍后发现此问题的人的一些后续信息:
关键信息由Colonel Thirty Two发布,C模块扩展和FFI代码可以轻松超出限制。 (以及引用的lua列表帖子提醒超出限制的普通Lua表对垃圾收集的速度非常慢)
我花了一些时间把各个部分拉到一起来访问和保存/加载我的对象,所以在这里它是在一个地方:
我在https://github.com/neomantra/lds使用lds作为起点,特别是1-D数组代码。
这使用了torch.save(),因为它不知道如何编写新对象。对于每个对象,我添加了下面的代码(使用Array作为示例):
function Array:load(inp)
for i=1,#inp do
self._data[i-1] = tonumber(inp[i])
end
return self
end
function Array:serialize ()
local siz = tonumber(self._size)
io.write(' lds.ArrayT( ffi.typeof("double"), lds.MallocAllocator )( ', siz , "):load({")
for i=0,siz-1 do
io.write(string.format("%a,", self._data[i]))
end
io.write("})")
end
请注意,我的应用程序专门使用了双精度和malloc(),因此更好的实现将在上面存储和使用它们而不是硬编码。
然后正如PiL和其他地方所讨论的那样,我需要一个能够处理对象的序列化器:
function serialize (o)
if type(o) == "number" then
io.write(o)
elseif type(o) == "string" then
io.write(string.format("%q", o))
elseif type(o) == "table" then
io.write("{\n")
for k,v in pairs(o) do
io.write(" ["); serialize(k); io.write("] = ")
serialize(v)
io.write(",\n")
end
io.write("}\n")
elseif o.serialize then
o:serialize()
else
error("cannot serialize a " .. type(o))
end
end
这需要包装:
io.write('do local _ = ')
serialize( myWeirdTable )
io.write('; return _; end')
然后可以用
重新加载它的输出local myWeirdTableReloaded = dofile('myWeirdTableSaveFile')
请参阅dLile()
中的PiL(Lua书中的编程)希望能帮助别人!
答案 2 :(得分:3)
您可以使用torch tds模块。来自自述文件:
不依赖于Lua内存分配器的数据结构,也不受Lua垃圾收集器的限制。
只能存储C类型:支持的类型当前是数字,字符串,数据结构本身(请参阅嵌套:例如,可以使用包含Hash或Vec的Hash),以及割炬张量和存储。所有数据结构都可以存储异构对象,并支持火炬序列化。