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

概述是我正在开发原型代码以了解我的问题空间,并遇到“PANIC:unprotected error in call to Lua API(not enough memory)”错误。我正在寻找绕过此限制的方法。

环境底线是Torch,这是一个在LuaJIT上运行的科学计算框架,而LuaJIT又运行在Lua上。我需要Torch,因为我最终想要使用GPU上的神经网络来解决我的问题,但为了到达那里,我需要一个良好的问题表示来提供给神经网络。我停留在Centos Linux上,并且我怀疑尝试从源代码重建所有的32位模式(据说这会将LuaJIT内存限制扩展到4GB)将是一场噩梦(如果可以的话),因为所有的库都需要这样做。

问题空间本身可能不是特别相关,但在概述中,我有计算距离并将这些距离“分组”(即制作直方图),以尝试确定最有用的范围的点的数据文件。方便的是,我可以创建各种不同的bin和torch.save()混乱的计数表,然后稍后拾起并使用不同的归一化等检查,因此经过一个月的尝试,我发现这非常简单和强大。

我可以在每个具有15个bin的3个距离的情况下使其工作(15x15x15加开销),但这只是通过添加显式的garbagecollection()调用并使用fork()/wait()来实现的。每个数据文件,以便外循环将继续运行,即使其中一个数据文件(其中有几千个)仍然超出内存限制并使子项崩溃。随着每个成功的子进程现在必须读取,修改和写入当前的二进制计数集,这变得更加痛苦-我的适用于此的最大文件目前为36mb。我想要更大(更多的bin),而且真的希望只将计数保存在我无法访问的15个gigs的RAM中。

因此,这是我考虑过的一些路径;如果您可以确认/否认这些路径中的任何一条会/不会将我带出1gb界限,或只会在它内部提高我的效率,请务必发表评论。如果您可以提出我没有考虑的另一个方法,请务必发表评论。

  • 我是否错过了启动可以从中读取任意表格的Lua进程的方法?毫无疑问,我可以把我的问题分成更小的部分,但解析从上下文返回表格(如从调用到另一个Lua脚本的系统调用)似乎容易出错,并且写入/读取小的中间文件将会是大量的磁盘I / O。

  • 我是否错过了在高内存中存储并访问表格的模块?这似乎是我真正想要的,但仍未找到

  • 是否可以将FFI C数据结构放在1GB之外?似乎这不可能是情况,但我确实缺乏对首先导致限制的真实理解。我怀疑这只会为一些已经超出原型阶段的部分提供比通用Lua表更高的效率?(除非我为每个更改编写了一堆代码)

  • 当然,我可以通过使用C编写扩展(Torch似乎支持应该超出限制的网络),但我的简短调查引用了“lightuserdata”指针--是否意味着更常规的扩展也不会超出1GB?为了这应该是一个原型练习的事情,这也似乎具有重大的开发成本。

我很了解C,所以使用FFI或扩展路径并不困扰我-但是,我从经验中知道,在这种方式中封装算法既可以是非常优美的,也可以是非常痛苦的,因为有两个地方可以隐藏错误。在栈中处理包含表中表的数据结构似乎也不是很好。在我做出这种努力之前,我想确定最终结果是否真的会解决我的问题。

感谢阅读长篇文章。

点赞
用户646619
用户646619

只有由LuaJIT本身分配的对象才被限制为2GB内存。这意味着,表、字符串、完全的userdata(即非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中要实现的新GC不会有这个限制,但我最近没有听到任何关于它的开发的消息。

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

2014-11-19 14:44:31
用户2783487
用户2783487

以下是那些在未来发现此问题的人的跟进信息:

关键信息如Colonel Thirty Two所发表的,即C模块扩展和FFI代码很容易超出限制。(而引用的Lua列表帖子也提醒了超出限制的纯Lua表将非常缓慢地回收)

我花了一些时间来整合访问和保存/加载我的对象,所以在这里:

我使用lds作为起点,在https://github.com/neomantra/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(),因此更好的实现将存储和在self中使用这些值,而不是像上面那样硬编码。

然后,如在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')

有关dofile(),请参见PiL(Lua编程书)

希望这能帮助到某人!

2014-11-21 11:54:34
用户4748557
用户4748557

你可以使用 torch tds 模块。在 README 中,它描述道:

这些数据结构不依赖 Lua 的内存分配器,也不受 Lua 垃圾回收器的限制。

仅支持 C 类型的存储:当前支持的类型有数字、字符串、数据结构本身(参见嵌套:例如可以在哈希中包含哈希或向量),以及 torch 张量和存储。所有数据结构都可以存储异构对象,并支持 torch 序列化。

2017-01-19 17:33:35