将一个表格设置为空还是将表格的所有元素设置为nil更好?

最近我参与一次代码审查时,出现了一个关于一段 lua 代码的问题。这段代码会清空缓存并用一些数据重新初始化:

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

-- 在这里重新初始化

上面的循环代码是否替换为以下代码呢?

fileTable = { }

-- 在这里重新初始化
点赞
用户936986
用户936986

在 Lua 中分配一个新表是一项昂贵的操作(对于几乎任何动态语言中的任何对象分配也是如此)。此外,不断将新创建的数据交给垃圾收集器 GC 会给性能和内存带来额外的负担,因为每个创建的表将一直在内存中,直到 GC 真正来索取它。

你的示例技巧是用表格中所有元素的时间来换取这些劣势。这将始终是一种节省内存的方式,而且根据元素数量的不同,可能也是一种提高性能的方式。

2013-04-26 15:24:23
用户107090
用户107090

除非你有证据证明相反,否则最好相信 Lua 的垃圾回收:当需要时只需创建一个新的空表。

2013-04-26 15:53:40
用户2198692
用户2198692

这是由于表的调整和重新哈希开销。当创建一个表时,它是空的。当你插入一个元素时,会发生重新哈希,表的大小增加到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

结果很有趣。在Lua 5.1上运行test(4, 10000000),空表和nil的时间分别为10秒和7秒。超过32个元素的表,空表版本更快(表越大,差距越大)。test(128,400000),空表的时间是5秒,nil的时间是9秒。

现在在LuaJIT上,分配和垃圾回收操作相对较慢,运行test(1024, 1000000),空表的时间是7秒,nil的时间是3秒。

附注:请注意纯Lua和LuaJIT之间的性能差异。对于1024个元素的表,纯Lua需要大约20秒来执行100,000次测试迭代,而LuaJIT在10秒内执行了1,000,000次迭代!

2013-04-26 16:25:56