Нет, нет в настоящее время (< = 5.3.x).
Но есть куча вещей, которые вы можете сделать, чтобы улучшить ситуацию:
... Я бегу из памяти быстро, как сборщик мусора не может угнаться.
GC отслеживает размеры полных пользовательских данных (и соответственно увеличивает задолженность GC), но не знает их для легких пользовательских данных. Если вы lua_pushlightuserdata
(L, ptr);
значение, выделенное в другом месте (по malloc
, mmap
и т. Д.), GC «видит» размер нуля. Если вы используете lua_newuserdata
(L, size)
для выделения, GC знает полный размер.
Вы, вероятно, использовать lua_newuserdata
для тонкой оберточной структуры (для получения __gc
) вокруг данных «тучные» ссылочных (невидимо Lua) из этой структуры, соответственно GC видит несколько килобайт используемой памяти, пока вы коробления гигабайты. Если возможно, попробуйте переключить распределитель на внутреннюю вещь от malloc
до lua_newuserdata
. (Если вам не нужно делиться данными через состояния Lua и/или нуждаться в специальном выравнивании или иметь другие ограничения ...) Это должно решить большинство проблем. (Он все равно будет постоянно собирать материал, но по крайней мере он больше не будет OOM.)
В противном случае вставляйте явные вызовы GC-шага перед каждым распределением (с размером $ ((размер невидимого выделения в килобайтах)). , чтобы эмулировать большинство из того, что у вас было бы с полными пользовательскими данными), и играть с мультипликатором шага GC и/или паузой.
Если этого недостаточно, вы можете попробовать более сложные вещи, которые требуют uglifying кода.
Операторы metemethods знают только об этих двух аргументах, поэтому им всегда нужно создать новое целевое значение. Если вы используете функции вместо операторов, вы можете передать существующее заброшенное значение в качестве цели, например.
local psi_ij, psi_ijj
for j=1,N do
for i=j,N do
psi_ij = qmul(psi[i],psi[j], psi_ij)
psi_ijj = qmul(psi_ij,psi[j], psi_ijj)
psi[j] = qsub(psi[j],psi_ijj, psi[j])
end
end
где qadd
, qsub
, qmul
и т.д. бы (a,b[, target])
и повторное использование target
если дано или иным образом выделить новое хранилище. (Это уменьшит цикл из распределений N^2 до двух распределений.) Хороший трюк, если вы определяете их таким образом, заключается в том, что вы также можете использовать те же функции, что и метамотоды операторов. (target
просто всегда будет отсутствовать, так что если вы не заботитесь о распределении вы можете просто использовать операторы.)
(Если psi[k]
не все имеют одинаковый размер, так что размеры промежуточных значений меняются, вы можете явно указать target
как бесплатную в математической функции, если она несовместима, то, возможно, она будет объединена с хранилищем для поиска, как показано ниже:
Другой вариант заключается в том, чтобы хранить заброшенные значения и явно указывать значения как заброшенные, повторно использовать существующие значения. Поверхностные непроверенные коды:
-- hidden in implementation somewhere
-- MT to mark per-size stores as weak so GC can collect values
local weak = { __mode = "k" }
-- storage of freed values, auto-create per-size storage table
local freed = setmetatable({ }, {
__index = function(t, k)
local v = setmetatable({ }, weak)
t[k] = v
return v
end
})
-- interface to that storage
-- replace size(v) by some way to get a size/layout descriptor
-- (e.g. number or string giving size in bytes or dimensions or ...)
function free(v, ...)
freed[size(v)][v] = true -- mark as free
if ... then return free(...) end
end
-- return re-usable value of size/layout vsize, or nil if allocation needed
function reuse(vsize)
local v = next(freed[vsize])
if not v then return nil end
freed[vsize][v] = nil -- un-mark
return v
end
С этим, ваш Распределитель должен тогда сначала проверить reuse
и только в том случае, что возвращает ноль фактически выделить новое значение. И ваш код должен быть изменен, как, например:
for j=1,N do
for i=j,N do
local psi_j = psi[j]
local psi_ij = psi[i] * psi_j
local psi_ijj = psi_ij * psi_j
psi[j] = psi[j] - psi_ijj
free(psi_j, psi_ij, psi_ijj)
end
end
С измененным определением свободного, как
local temp = setmetatable({ }, { __mode = "v" })
function free(v)
table.insert(temp, v)
if #temp > 2 then
local w = table.remove(temp, 1)
freed[size(w)][w] = true
end
return v
end
добавить достаточно задержек, что вы можете написать выражение инлайн (по времени значение на самом деле помечается как свободное, бинарная операция была проведена оценка, если ничего не происходит между ними):
local _ = free
for j=1,N do
for i=j,N do
local psi_j = psi[j]
psi[j] = psi_j - _(_(psi[i] * psi_j) * psi_j)
free(psi_j)
end
end
... который выглядит лучше, и избавляется от необходимости отслеживать промежуточные значения, но это пр etty легко сломать, случайно высвободив слишком рано (например, вместо того, чтобы высвобождать psi[j]
в конце, делать будет разорвать.)
Хотя этот последний вариант (почти) восстанавливает нормальные выражения, это действительно не такая хорошая идея. И тот, который до этого не намного лучше, чем использование функций вместо операторов, но требует более хрупкой бухгалтерской отчетности и медленнее. Поэтому, если вы решите микроуправление распределениями, я бы предложил использовать вариант в первой версии. (У вас также могут быть функции как методы, возможно, с аргументами, переключаемыми вокруг (target:add(a,b)
), как это делает факел (но тогда вам нужно явно выделить target
в коде и знать размеры до того, как вы сделаете операцию ...) Все эти вариации сосать по-разному. Постарайтесь не заходить так далеко, и не удается построить тот, который вам не нравится.)
Наверное, нет. Но вы можете добавить функцию 'destroy' * * в свои userdatas, которая освобождает данные раньше. – immibis
Я думаю, что вы должны называть сборщик мусора постепенно, когда вы идете, вместо того, чтобы пытаться сделать все это одним выстрелом. Например, проверьте все параметры http://www.lua.org/manual/5.3/manual.html#pdf-collectgarbage. Я бы попытался использовать опцию «шаг» с различными значениями и посмотреть, могу ли я получить хорошие результаты –