lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



On 7-Aug-05, at 3:46 AM, Uli Kusterer wrote:

First and foremost, I originally just tried the part inside the loop. When that didn't call the destructor, I thought maybe there's a threshold for the GC how many objects have to be there to be considered worth collecting, so I put it a loop around it.

Indeed there is, and 100 iterations is probably not to trigger it. You must call lua_close(L) in order to get the garbage collector to finalise all objects when the state is about to be destroyed.

My point was simply that using a global and setting it to nil is not going to trigger gc any earlier than using a local. (I'm being influenced here by the globals vs. locals mailing list thread, I guess.)

That is, in the loop:

for i = 1, 100
  local a = Room.new()
end

all of the a's are going to be stored in the same stack slot, overwriting the previous one, almost exactly the same as the equivalent C construct:

for (i = 1; i <= 100; i++) {
  Room* a = newRoom();
}

with three exceptions:

1) In Lua, unlike C, 'i' is also local to the loop.
2) In Lua, unlike C, overwriting 'a' will not cause a memory leak.
3) Although this particular example doesn't show it, Lua allows 'a' to be closed into a function closure, in which case it will be reallocated onto the heap (in effect). A simple meaningless example might help:

log = {}
for i = 1, 100 do
  local logi = math.log(i)
  log[i] = function(x)
             return math.log(x) / logi
           end
end

> = log[2](8)
3
> = log[4](8)
1.5
> = log[10](8)
0.90308998699194

Although logi is initially stack allocated, it's use as an unbound variable in the anonymous function causes it to be evicted to the heap at the end of each iteration of the for loop, so that it has a different value for each element of the table 'log'.

While I'm blathering on, note that the unbound variable 'math' in the anonymous function is a global, since it is unbound in the program; consequently, it needs to be looked up each time a log function is called, followed by a table lookup of the key 'log'. This is presumably unnecessary, and a considerable speed-up can be achieved by simply looking it up once:

do -- provide a scope for math_log
  local math_log = math.log
  log = {}
  for i = 1, 100 do
    local logi = math_log(i)
    log[i] = function(x) return math_log(x) / logi end
  end
end

It is this speed up that may have motivated the creation of the table of log functions, rather than simply defining:

function mylog(x, base)
  return math.log(x) / math.log(base)
end

Of course, that function could also have been optimised by the same technique:

do
  local math_log = math.log
  function mylog(x, base)
    return math_log(x) / math_log(base)
  end
end