gpt4 book ai didi

c++ - Lua中具有大用户数据的有效垃圾收集

转载 作者:搜寻专家 更新时间:2023-10-31 02:15:32 24 4
gpt4 key购买 nike

我已经在Lua(C API)之上实现了一个代码来解决量子力学中的问题。它将量子力学运算符和波动函数添加到脚本语言中。到现在为止还挺好。挑战是wave函数的用户数据可能很大(该用户数据包含指向1Mb和1Gb之间的数组的指针)我添加了标准垃圾收集方法,对于简单的情况,它们也可以工作。

    static int LuaWavefunctionDestroy(lua_State * L)
{
WaveFunctionType *psi = (WaveFunctionType*) luaL_checkudata(L, 1, "Wavefunction_Type");
WaveFunctionFree(psi);
return 0;
}

和使用_gc的元方法调用
    static const struct luaL_Reg Wavefunction_meta[] = {
{"__add", LuaWavefunctionAdd},
{"__sub", LuaWavefunctionSub},
{"__mul", LuaWavefunctionMul},
{"__div", LuaWavefunctionDiv},
{"__unm", LuaWavefunctionUnm},
{"__index", LuaWavefunctionIndex},
{"__newindex", LuaWavefunctionNewIndex},
{"__tostring", LuaWavefunctionToString},
{"__gc", LuaWavefunctionDestroy},
{NULL, NULL}
};

但是,如果我现在在Lua中运行以下代码
    for j=1,N do
for i=j,N do
psi[j] = psi[j] - ( psi[i] * psi[j] ) * psi[j]
end
end

由于psi是具有数(10-100)个波函数的表(数组),由于垃圾收集器无法跟上,因此我很快用完了内存。

更糟糕的是,我已经注册了数千个字符串和常量(数字),所以完整的垃圾回收需要经历许多变量

有没有一种方法可以对特定对象或仅对用户数据运行垃圾回收?

最佳答案

否,当前不在(<= 5.3.x)。

但是您可以采取很多措施来改善这种情况:

…I run out of memory quickly as the garbage collector can not keep up.



GC会跟踪完整用户数据的大小(并相应地增加GC债务),但对于轻量级用户数据并不知道。如果你
lua_pushlightuserdata ( L, ptr );一个在其他地方分配的值(通过 mallocmmap等),GC“看到”的大小为零。如果使用 lua_newuserdata ( L, size )进行分配,则GC知道完整大小。

您可能使用 lua_newuserdata来处理从该结构引用(不可见地引用到Lua)的“胖”数据周围的瘦包装器结构(以获取 __gc),因此,当您占用千兆字节时,GC会看到几千字节的已用内存。如果可能,请尝试将内部事物的分配器从 malloc切换为 lua_newuserdata。 (除非您需要在Lua州之间共享数据和/或需要特殊的对齐方式或具有其他约束条件...),这应该可以解决大多数问题。 (它仍然会不断收集东西,但至少它不会再OOM了。)

失败的话,在每个分配(带有$((不可见分配的大小,以千字节为单位))步骤之前插入显式GC步骤调用,以模拟您将拥有完整用户数据的大部分内容),然后使用GC步骤乘数和/或暂停。



如果这还不够的话,您可以尝试更多涉及到丑陋代码的事情。

运算符元​​方法仅了解两个参数,因此它们始终必须创建一个新的目标值。如果您使用函数而不是运算符,则可以将现有的废弃值作为目标传递,例如

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

其中 qaddqsubqmul等将采用 (a,b[, target])并重新使用 target(如果给定的话),否则将分配新的存储空间。 (这会将循环从N ^ 2分配减少到两个分配。)如果以此方式定义它们,则一个不错的技巧是,您还可以使用与运算符元方法相同的函数。 ( target将始终始终不存在,因此,如果您不关心分配,则可以使用运算符。)

(如果 psi[k]的大小都不同,以至于中间值的大小各不相同,则可以在不兼容的数学函数中将 target明确标记为free,然后将其与查找存储结合使用,如下所示:)

另一种选择是拥有一个废弃值的存储并明确地将值标记为已废弃,然后让分配器最好重新使用现有的废弃值。粗略的未经测试的代码:

-- 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,并且仅当返回nil时才实际分配一个新值。而且您的代码将必须进行更改,例如:

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

...看起来更好,并且无需跟踪中间值,但是很容易因意外地过早释放而中断(例如,而不是在最后释放 psi[j]时,执行 psi[j] = _(psi[j]) - _(_(psi[i] * psi[j]) * psi[j])会中断。)

尽管最后一个变体(几乎)恢复了看起来正常的表达式,但实际上并不是一个好主意。而在那之前,它并没有比使用函数而不是运算符好得多,但是需要更脆弱的簿记且速度较慢。因此,如果您决定微观管理分配,我建议在第一个版本上使用变体。 (您也可以将这些函数作为方法使用,可能像torch一样将参数切换( target:add(a,b))(但是您需要在代码中显式分配 target,并且需要在执行操作之前知道大小...)变化会以不同的方式吸引人。请尽量避免走得太远,否则会造成您最不喜欢的变化。)

关于c++ - Lua中具有大用户数据的有效垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38391767/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com