Optimization 将表设置为空还是将表的所有元素设置为零更好?

Optimization 将表设置为空还是将表的所有元素设置为零更好?,optimization,lua,Optimization,Lua,在我最近的一次代码审查中,出现了一个关于lua代码的问题。正在讨论的代码正在刷新缓存并使用一些数据重新初始化它: for filename,_ in pairs(fileTable) do fileTable[filename] = nil end -- reinitialize here 有什么理由不应该用这个替换上面的循环吗 fileTable = { } -- reinitialize here 在Lua中,分配一个新表是一个代价高昂的操作(对于几乎任何动态语言中的任何对象

在我最近的一次代码审查中,出现了一个关于lua代码的问题。正在讨论的代码正在刷新缓存并使用一些数据重新初始化它:

for filename,_ in pairs(fileTable) do
    fileTable[filename] = nil
end

-- reinitialize here
有什么理由不应该用这个替换上面的循环吗

fileTable = { }

-- reinitialize here

在Lua中,分配一个新表是一个代价高昂的操作(对于几乎任何动态语言中的任何对象分配都是如此)。此外,不断地将新创建的表“丢失”给GC会给性能和内存带来额外的压力,因为每个创建的表都将仍然在内存中,直到GC真正声明它为止


在您的示例中,该技术将这些缺点与显式删除表中所有元素所需的时间进行权衡。这将始终是一种节省内存的方法,并且根据元素的数量,通常也可能是一种性能改进

除非有其他证据,否则最好相信Lua的垃圾收集:只要在需要时创建一个新的空表。

这是由于表大小调整/重新刷新开销造成的。创建表时,该表为空。当您插入一个元素时,将进行重新刷新,表大小将增长到1。插入另一个元素时也会发生同样的情况。规则是,每当没有足够的空间(在数组或散列部分中)容纳另一个元素时,表就会增长。新尺寸是2的最小幂,可以容纳所需数量的元素。例如,如果表中包含0、1、2、4、8等元素,则在插入元素时会发生重缓存

现在,您描述的技术保存了这些重灰烬,因为Lua不会收缩表。因此,当您有频繁的填充/刷新表操作时,按照示例中的方式进行操作(性能方面)比创建空表更好

更新:

我做了一个小测试:

local function rehash1(el, loops)
    local table = {}
    for i = 1, loops do
        for j = 1, el do
            table[j] = j
        end
        for k in ipairs(table) do table[k] = nil end
    end
end

local function rehash2(el, loops)
    for i = 1, loops do
        local table = {}
        for j = 1, el do
            table[j] = j
        end
    end
end


local function test(elements, loops)
    local time = os.time();
    rehash1(elements, loops);
    local time1 = os.time();
    rehash2(elements, loops);
    local time2 = os.time();

    print("Time nils: ", tostring(time1 - time), "\n");
    print("Time empty: ", tostring(time2 - time1), "\n");

end
结果很有趣。在Lua5.1上运行
test(4100000)
,得到7秒的零和10秒的空。对于大于32个元素的表,空版本更快(表越大,差异越大)<代码>测试(128400000)对零给出9秒,对空给出5秒

现在在LuaJIT上,alloc和gc操作相对较慢,运行
test(10241000000)
时,nils为3秒,清空为7秒


请注意普通Lua和LuaJIT之间的性能差异。对于1024个元素表,纯Lua在大约20秒内进行了100000次测试迭代,LuaJIT在10秒内进行了1000000次迭代

为了减少内存分配-释放操作?除了回答中描述的性能问题之外。。。如果该表由程序的其他部分以其他名称使用,则循环是绝对必要的。如果执行赋值,则会丢失到表对象的链接,其他名称仍指向同一个完整表,而循环会为每个人清除该表。不建议使用变量名作为
table
。它破坏了其他函数,如
table.concat
:)@hjpotter,仅在该函数中,因为它是本地函数。这是不可取的,如果你不知道它(范围)。