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

Posted

技术标签:

【中文标题】如何在 Linux 上超过 64 位 LuaJIT 的 1gb 内存限制?【英文标题】:How to get past 1gb memory limit of 64 bit LuaJIT on Linux? 【发布时间】:2015-01-16 20:54:33 【问题描述】:

概述是我正在对代码进行原型设计以了解我的问题空间,并且我遇到了“恐慌:调用 Lua API 时出现不受保护的错误(内存不足)”错误。我正在寻找绕过此限制的方法。

环境底线是 Torch,一个运行在 LuaJIT 上的科学计算框架,LuaJIT 运行在 Lua 上。我需要 Torch 是因为我最终想在 GPU 上解决我的神经网络问题,但要实现这一点,我需要一个很好的问题表示来提供给网络。我(卡住)在 Centos Linux 上,我怀疑尝试以 32 位模式从源代码重建所有部分(据报道,这会将 LuaJIT 内存限制扩展到 4gb)如果它对所有的都有效,那将是一场噩梦图书馆。

问题空间本身可能不是特别相关,但总的来说,我有一些点的数据文件,我计算它们之间的距离,然后对这些距离进行 bin(即制作直方图)以尝试计算出最有用的范围。我可以方便地创建复杂的 Lua 表,其中包含各种 bin 和 torch.save() 计数的混乱,然后将其拾起并使用不同的标准化等进行检查 - 所以在玩了一个月后,我发现这是真的很容易和强大。

我可以让它查看最多 3 个距离,每个距离 15 个 bin(15x15x15 加上开销),但这只能通过添加显式的garbagecollection() 调用并为每个数据文件使用 fork()/wait() 以便外部如果一个数据文件(几千个)仍然超出内存限制并使子进程崩溃,则循环将继续运行。这变得更加痛苦,因为现在每个成功的子进程都必须读取、修改和写入当前的 bin 计数集——而我目前最大的文件是 36mb。我想要更大(更多垃圾箱),并且真的更愿意将计数保留在我似乎无法访问的 15 GB RAM 中。

所以,这是我想到的一些路径;如果您可以确认/否认其中任何一个将/不会让我超出 1gb 边界,或者只会提高我在其中的效率,请发表评论。如果您能提出另一种我没有想到的方法,请发表评论。

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

我是否缺少高内存模块中的存储和访问表?这似乎是我真正想要的,但还没有找到它

FFI C 数据结构可以放在 1gb 之外吗?情况似乎并非如此,但我当然对首先导致限制的原因缺乏全面了解。我怀疑这只会让我比通用 Lua 表的效率提高一些,因为它已经超出了原型设计的范围? (除非我对每次更改都进行大量编码)

1234563更正常的扩展也不会超出 1gb 吗?这似乎也为原型设计工作带来了沉重的开发成本。

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

感谢您阅读这篇长文。

【问题讨论】:

这个问题是否仍然与最新的 luajit 版本相关? 是的,据我所知 - 从变更日志中看,自 2014 年以来这没有太大变化。不幸的是,随着 Torch 切换到维护模式,我现在正在从Lua :-( x64/LJ_GC64: Add JIT compiler backend 从这里:github.com/LuaJIT/LuaJIT/blob/v2.1/doc/changes.html。刚刚测试,能分配6GB内存。不知道稳定性如何。 【参考方案1】:

只有由 LuaJIT 自己分配的对象才被限制在前 2GB 内存中。这意味着分配有ffi.new 的表、字符串、完整用户数据(即不是lightuserdata)和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

【讨论】:

如果我在一个进程中有多个 lua_State 怎么办?此限制适用于单个 lua_State 还是全局? 我认为它是全球性的,但您可能需要在 LuaJIT 邮件列表中询问以确保。【参考方案2】:

以下是一些后续信息,供以后发现此问题的人使用:

32 上校发布的关键信息是,C 模块扩展和 FFI 代码很容易超出限制。 (并且引用的 lua 列表帖子提醒,超出限制的普通 Lua 表将非常缓慢地进行垃圾收集)

我花了一些时间将各个部分拼凑在一起以访问和保存/加载我的对象,所以它在一个地方:

我使用https://github.com/neomantra/lds 的lds 作为起点,尤其是一维数组代码。

这使用 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 编程一书)

希望对某人有所帮助!

【讨论】:

【参考方案3】:

您可以使用torch tds 模块。来自自述文件:

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

只能存储 C 类型:当前支持的类型是数字、字符串、数据结构本身(参见嵌套:例如,可以有一个包含 Hash 或 Vec 的 Hash),以及 Torch 张量和存储。所有数据结构都可以存储异构对象,并支持torch序列化。

【讨论】:

以上是关于如何在 Linux 上超过 64 位 LuaJIT 的 1gb 内存限制?的主要内容,如果未能解决你的问题,请参考以下文章

最新LuaJIT 32/64 位字节码,从编译到使用全纪录

在Unity中使用 luajit 64位加密

在 32 位 linux 内核上使用 c++ 在堆上分配超过 2GB

linux磁盘分区

如何使用 64 位浏览器和 64 位 java 插件在 64 位 Linux 上获取 32 位 JRE 路径

在 64 位 linux 上安装 python 32 位