如何在Linux上超过64位LuaJIT的1GB内存限制?

时间:2014-11-19 10:59:00

标签: lua luajit torch

概述是我正在编写代码以了解我的问题空间,并且我遇到了'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或扩展路线并没有打扰我 - 但我从经验中知道,以这种方式封装算法既可以非常优雅,又可以通过两个地方来隐藏错误。处理包含堆栈中表格中的表格的数据结构似乎也不是很好。在我做出这些努力之前,我想确定最终结果真的能解决我的问题。

感谢阅读长篇文章。

3 个答案:

答案 0 :(得分:9)

只有LuaJIT本身分配的对象仅限于前2GB的内存。这意味着表,字符串,完整用户数据(即不是lightuserdata)和分配有ffi.new的FFI对象将计入限制,但分配有mallocmmap等的对象是不受此限制(无论是否由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不会有这个限制,但我最近没有听到任何关于它的发展的消息。

来源:http://lua-users.org/lists/lua-l/2012-04/msg00729.html

答案 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),以及割炬张量和存储。所有数据结构都可以存储异构对象,并支持火炬序列化。