Как преодолеть ограничение на 1 ГБ памяти для 64-битного LuaJIT на Linux?

Обзор - это прототип кода, чтобы понять мое проблемное пространство, и я запускаю "PANIC: незащищенная ошибка при вызове ошибок Lua API (недостаточно памяти)". Я ищу способы обойти этот предел.

Нижняя строка среды - это факел, научная вычислительная среда, которая работает на LuaJIT, а LuaJIT работает на Lua. Мне нужен факел, потому что я в конечном итоге хочу забить мою проблему с нейронными сетями на графическом процессоре, но чтобы добраться туда, мне нужно хорошее представление о проблеме, чтобы прокормить сети. Я (застрял) на Centos Linux, и я подозреваю, что попытка перестроить все части из источника в 32-битном режиме (об этом сообщается, чтобы продлить ограничение памяти LuaJIT до 4 ГБ) будет кошмаром, если он вообще работает для всех библиотеки.

Само проблемное пространство, вероятно, не имеет особого значения, но в обзоре у меня есть файлы данных о точках, которые я вычисляю расстояния между ними, а затем bin (т.е. делают гистограммы) этих расстояний, чтобы попытаться выработать наиболее полезные диапазоны. Удобно, я могу создавать сложные таблицы Lua с различными наборами бункеров и torch.save() беспорядок отсчетов, затем забрать его позже и проверить с различными нормализациями и т.д. - поэтому после одного месяца игры я нахожу это действительно легкий и мощный.

Я могу заставить его работать, глядя на 3 расстояния с 15 бинами каждый (15x15x15 плюс накладные расходы), но это только путем добавления явных вызовов garbagecollection() и использования fork()/wait() для каждого файла данных, так что внешний цикл будет продолжать работать, если один файл данных (из нескольких тысяч) все еще удаляет ограничение памяти и выдает его из строя. Это становится еще более болезненным, так как каждый успешный дочерний процесс теперь должен читать, изменять и записывать текущий набор подсчетов bin, а мои самые большие файлы для этого в настоящее время составляют 36 Мб. Я хотел бы увеличить размер (больше бункеров), и я бы предпочел просто держать подсчеты в 15 гигабайтах оперативной памяти, к которым я не могу получить доступ.

Итак, вот несколько путей, о которых я подумал; пожалуйста, сделайте комментарий, если вы можете подтвердить/опровергнуть, что любой из них будет/не получит меня за пределами границы 1gb или просто улучшит мою эффективность в нем. Пожалуйста, сделайте комментарий, если вы можете предложить другой подход, о котором я не думал.

  • Мне не хватает способа запустить Lua-процесс, из которого я могу прочитать произвольную таблицу? Без сомнения, я могу разбить свою проблему на более мелкие части, но разбор таблицы возврата из stdio (как из системного вызова на другой Lua script) кажется подверженным ошибкам, а для записи/чтения небольших промежуточных файлов будет много дисков i/о.

  • Мне не хватает модуля с файлом stash-and-access-table-in-high-memory? Это похоже на то, что я действительно хочу, но еще не нашел его

  • Можно ли создавать структуры данных FFI C за пределами 1gb? Не похоже, что так будет, но, конечно, мне не хватает полного понимания того, что вызывает лимит в первую очередь. Я подозреваю, что это только даст мне повышение эффективности по сравнению с типичными таблицами Lua для нескольких частей, которые вышли за рамки прототипирования? (если я не делаю кучу кодирования для каждого изменения)

  • Конечно, я могу выйти, написав расширение на C (факел, похоже, поддерживает сети, которые должны выйти за пределы), но в моем кратком исследовании появляются ссылки на указатели "lightuserdata" - означает ли это что более нормальное расширение не выйдет за пределы 1gb? Это также похоже на то, что у него большие затраты на разработку того, что должно быть упражнением для прототипирования.

Я знаю, что C хорошо, поэтому переход на FFI или расширение не беспокоит меня, но я знаю по опыту, что инкапсуляция алгоритмов таким образом может быть как очень изящной, так и очень болезненной с двумя местами, чтобы скрыть ошибки. Работа с структурами данных, содержащими таблицы в таблицах в стеке, также не очень велика. Прежде чем я сделаю это, я хотел бы быть уверенным, что конечный результат действительно решит мою проблему.

Спасибо за чтение длинного сообщения.

Ответ 1

Только объект, выделенный самим LuaJIT, ограничен первым объемом 2 ГБ памяти. Это означает, что таблицы, строки, полные пользовательские данные (то есть не lightuserdata) и объекты FFI, выделенные с помощью ffi.new, будут отсчитываться до предела, но объекты, выделенные с помощью 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

Новый GC, который будет реализован в LuaJIT 3.0 IIRC, не будет иметь этого предела, но я не слышал никаких новостей о его развитии в последнее время.

Источник: http://lua-users.org/lists/lua-l/2012-04/msg00729.html

Ответ 2

Ниже приведена следующая информация для тех, кто найдет этот вопрос позже:

Ключевая информация опубликована Colonel Thirty Two, что расширения модуля C и код FFI могут легко выйти за пределы. (и упоминаемое сообщение lua list напоминает, что обычные таблицы Lua, выходящие за пределы, будут очень медленными для сбора мусора)

Мне потребовалось некоторое время, чтобы собрать кусочки вместе для доступа и сохранения/загрузки моих объектов, поэтому здесь он находится в одном месте:

Я использовал lds в https://github.com/neomantra/lds в качестве отправной точки, в частности код 1-D Array.

Это сломалось с использованием 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

Обратите внимание, что мое приложение специально использует double и 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')

См. PiL (Программирование в книге Lua) для dofile()

Надеюсь, что это поможет кому-то!

Ответ 3

Вы можете использовать факел tds. Из README:

Структуры данных, которые не полагаются на распределитель памяти Lua и не ограничены сборщиком мусора Lua.

Можно сохранить только типы C: поддерживаемые типы в настоящее время являются числами, строками, самими структурами данных (см. вложение: например, возможно иметь хэш, содержащий Hash или Vec), а также тензоры и хранилища факелов. Все структуры данных могут хранить неоднородные объекты и поддерживать сериализацию факела.