Add a HashLength argument to table.create

There should be a HashLength argument for table.create which would preallocate the hash part of the table returned.

My use case: Creating a new table with all value from another table (with known size)

Example code which would use the extra argument
local tbl = {
    a = function()
        -- ...
    end,
    b = function()
        -- ...
    end,
    c = function()
        -- ...
    end,
    d = function()
        -- ...
    end,
    e = function()
        -- ...
    end
}
local len = 0
for _ in pairs(tbl) do len = len + 1 end
local function newtbl(...)
    local ntbl = table.create(0,nil,len)
    for i,v in pairs(tbl) do ntbl[i] = v end
    -- some more logic with ntbl
    return ntbl
end

Metatables could be used, but this isn’t faster once you consider slower access (must check the main table, and the the metatables __index metamethod), and you couldn’t override the field with nil since its part of the metatable.

Example code which uses metatables
local tbl = {
    a = function()
        -- ...
    end,
    b = function()
        -- ...
    end,
    c = function()
        -- ...
    end,
    d = function()
        -- ...
    end,
    e = function()
        -- ...
    end
}
local mtbl = {__index=tbl}
local function newtbl(...)
    local ntbl = {}
    -- some logic with ntbl
    return setmetatable(ntbl,mtbl)
end

The benefit for this use case would be pretty massive:
In the default lua interpreter with my extra functions added when I run this code

Code
local time = time
local pairs = pairs
local setmetatable = setmetatable
local create = table.create
local test1 = time()
do
	local tbl = {
		a=1,
		b=2,
		c=3,
		d=4,
		e=5
	}
	for _=1,100000 do
		local newtbl = {}
		for i,v in pairs(newtbl) do
			newtbl[i] = v
		end
		local _ = newtbl.a,newtbl.b,newtbl.c,newtbl.d,newtbl.e
	end
end
print(time()-test1)
local test2 = time()
do
	local tbl = {
		a=1,
		b=2,
		c=3,
		d=4,
		e=5
	}
	local mtbl = {__index=tbl}
	for _=1,100000 do
		local newtbl = setmetatable({},mtbl)
		local _ = newtbl.a,newtbl.b,newtbl.c,newtbl.d,newtbl.e
	end
end
print(time()-test2)
local test3 = time()
do
	local tbl = {
		a=1,
		b=2,
		c=3,
		d=4,
		e=5
	}
	local len = 0
	for _ in pairs(tbl) do
		len = len + 1
	end
	for _=1,10000 do
		local newtbl = create(0,len)
		for i,v in pairs(newtbl) do
			newtbl[i] = v
		end
		local _ = newtbl.a,newtbl.b,newtbl.c,newtbl.d,newtbl.e
	end
end
print(time()-test3)

(My table.create function just creates a table using the C api lua_createtable, and time gets the time using GetSystemTimeAsFileTime)

int table_create(lua_State*L){ /* table.create */
	lua_Integer narray = luaL_optinteger(L,1,0);
	lua_Integer nrec = luaL_optinteger(L,2,0);
	lua_createtable(L,narray,nrec);
	return 1;
}
lua_Integer gettime(void){
	FILETIME f;
	GetSystemTimeAsFileTime(&f);
	return (lua_Integer)f.dwLowDateTime+((lua_Integer)f.dwHighDateTime<<32LL);
}
int ltime(lua_State*L){ /* time */
	lua_pushnumber(L,(lua_Number)gettime()/10000000.0);
	return 1;
}

I get:

0.05004490073770284653
0.05104629974812269211
0.0100092003121972084

I presume in Luau a similar benefit could be achieved by adding a HashLength argument to table.create.

4 Likes