Re: Allocation sinking limits

  • From: "Vyacheslav Egorov" <dmarc-noreply@xxxxxxxxxxxxx> (Redacted sender "vegorov@xxxxxxxxxx" for DMARC)
  • To: luajit@xxxxxxxxxxxxx
  • Date: Sat, 1 Aug 2015 11:08:51 +0200

This happens because hash part of the res is no longer empty.

Which means luajit trace recorder can't guard on trace __newindex
invocation with a simple res.hmask == 0 comparison.

Instead it guards it with a real load checking that res[k] == nil:

0039 > tab TDUP {0x00050ed8}
0040 p32 FLOAD 0039 tab.array
0041 p32 AREF 0040 +2
0042 num CONV 0036 num.int
0043 num ASTORE 0041 0042
0044 p32 HREF 0007 0039 ;; this is where it escapes!
0045 > nil HLOAD 0044

You can move table_index to mt to workaround this.

In reality load forwarding could just eliminate HLOAD because it's
easy to see that there's no corresponding aliasing store.

However as far as I can see fwd_ahload doesn't take into account
aliasing between keys so it thinks there might be some aliasing.

// Vyacheslav Egorov


On Sat, Aug 1, 2015 at 9:25 AM, Denis Golovan <denis.golovan@xxxxxxxxx> wrote:

Hi all

I'd like to know if the behaviour I found is a bug.
If it is, consider the mail as bugreport :)
I've managed to create a small example, but I'd like to show several
iterations.

Case 1 works perfectly. I see {sink} tag for temporary table {1, i}.

--case 1
function table_index(t, ix)
return t[ix]
end

function tt()
local res = {}
for i=1,10000 do
res[ table_index({1, i}, 2) ] = 1
end
return res
end

jit.flush()
aa=tt()
--=================================================

Case 2 works fine again. The same idea, but via metatable.
I see {sink} tag for temporary table {1, ix}.

--case 2
function table_index(t, ix)
return t[ix]
end

mt = {
__newindex = function (a, ix, v)
rawset(a, table_index({1, ix}, 2), v)
end
}

function tt()
local res = {}
setmetatable(res, mt)
for i=1,10000 do
res[ i ] = 1
end
return res
end

jit.flush()
aa=tt()
--=================================================
Case 3 is almost production example and works fine again. The same
idea, but via metatable + indexing via table.
I see {sink} tag for temporary table {1, i}.

--case 3
function table_index(t, ix)
return t[ix]
end

mt = {
__newindex = function (a, ix, v)
rawset(a, table_index(ix, 2), v)
end
}

function tt()
local res = {}
setmetatable(res, mt)
for i=1,10000 do
res[ {1, i} ] = 1
end
return res
end

jit.flush()
aa=tt()
--=================================================
Case 4 is actually a production example. The same idea, but via
metatable + indexing via table + extra indirection.
Alas, allocation sinking stops working and each iteration TDUPs small
temporary table {1, i}.

--case 4
function table_index(t, ix)
return t[ix]
end

mt = {
__newindex = function (a, ix, v)
rawset(a, a.table_index(ix, 2), v) -- just additional indirection added
end
}

function tt()
local res = {}
res.table_index = table_index
setmetatable(res, mt)
for i=1,10000 do
res[ {1, i} ] = 1
end
return res
end

jit.flush()
aa=tt()

As you probably guessed, but I'm trying to emulate multidimensional
views for arrays on top of FFI 1-dimensional arrays, but allocation
sinking is crucial for decent speed here.


BR,
Denis


Other related posts: