使用2的指数倍扩容,针对单个表扩容其实并不频繁,比如100W个节点,最多扩容20次,2^20 > 100W。只是在表的前期会相对频繁,1、2、3、5、9条记录插入会触发扩容,所以要避免小表的频繁创建插入。
预先填充方式
a = os.clock()
for i = 1, 1000000 do
local tab = {["1"] = 1, ["2"] = 2, ["3"] = 3, ["4"] = 4, ["5"] = 5}
end
b = os.clock()
print(“timediff:”, b - a)
timediff: 0.53
动态扩容方式
a = os.clock()
for i = 1, 1000000 do
local tab = {}
tab["1"] = 1
tab["2"] = 2
tab["3"] = 3
tab["4"] = 4
tab["5"] = 5
end
b = os.clock()
print(“timediff:”, b - a)
timediff: 1.68
分析:可以看到预填充方式可以避免频繁的表扩容,cpu消耗是动态扩容的1/3。使用预填充的方式,在创建tab时,会直接分配一个8个元素的Node节点的内存,存放数据。而采用动态扩容的方式,第一次分配1个元素,然后随着数据的插入,会扩容到2个元素的内存,把原来数据重新hash到新分配的内存,释放老内存。同理,随着数据的插入,继而扩容到4个元素、扩容到8个元素...
2、rehash导致内存消耗演示
tab = {}
text = "diff mem"
collectgarbage("stop")
before = collectgarbage("count");
for i = 1, 100 do
tab[10000 + i] = i
local cur = collectgarbage("count")
print(text, cur - before)
before = cur ;
end
因为hashtable数据分配是0、1、2、4、8、16、32这些内存大小,所以在插入数据时分别在1、2、3、5、9、17、33时内存不够触发扩容,如上红色标记的点。可以看到在红色标记的点上,内存会有大幅扩大,而且基本是上一次大幅扩容的2倍关系。
问题一:在上述截图中,为什么在10、18、34这些地方出现了额外的内存消耗?