lua-users home
lua-l archive

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


steve donovan wrote:
> What would be useful is some kind of #line directive.  Not absolutely
> essential, if you keep an explicit line matching table and the script
> execution is under your control (as in LuaMacro) but definitely a good
> thing if you want to embed bytecode in your application.

The troublesome part are not the line numbers -- this can be
handled with the existing per-prototype line-number table (LuaJIT
actually compresses this table internally). But you'd really want
to allow multiple source filenames, too. That's where it gets
somewhat expensive.

> > - __gc for tables: bad idea, roadblock for further GC evolution
> 
> I can not immediately see the problem with allowing Lua objects
> control over their finalization; any issues that spring to mind?

The introduction of __gc for tables in Lua 5.2 is a fall-out from
the current design of the Lua garbage collector which keeps all
objects in chained lists. If you use setmetatable and it has a
__gc metamethod, then you simply rechain the table object into a
different list, which only handles finalizable objects. This list
needs to be treated differently, needs some extra traversals, the
objects need to be resurrected and rechained a couple of times,
etc. ...

Yes, one often forgets that finalization is an expensive concept!
It's not too bad here, since you only pay the toll when and _if_
you use this feature.

Since chained lists and inline mark bits are rather expensive on
modern CPUs, it's desirable to move to an arena-style GC with
segregated block management info. The higher throughput of this GC
design comes from the fact that many phases don't need to touch
the actual blocks, only the block management info. This is (e.g.)
only 1/64 the size and is more likely to fit into D-caches. Also,
most traversals are linear wrt. memory addresses, which allows
more effective pre-fetching.

However, if you want to implement finalizers, you'll still need to
keep track of finalizable objects somehow or, as a last resort,
check _every_ object whether it's finalizable/dead/finalized in
each GC phase. Segregating userdata would be a simple solution.
But that's pointless when you have to support __gc for tables,
too, since tables make up the majority of traversable objects.

Adding an O(N_all_objects) space- or time-overhead everywhere is
unacceptable. To reduce this to the (presumably much lower)
O(N_objects_with_gc), you'll need to jump through some hoops.
Doing in-object chaining is not attractive, since that extra
pointer will be left unused for most objects. Auxiliary arraylets
of pointers with lazy cleanup come to mind. Ouch #1.

The other issue is that setmetatable() will get more expensive for
_all_ tables. The cost of the extra indirection into the
nomm-cache of the metatable object might not be visible in an
interpreter, but it'll be noticeable when JIT-compiled. Working
around this is really tough -- bascially you'll need to add sealed
metatables and flush the code cache whenever this property is
violated. Ouch #2.

In any case: adding a rarely needed feature to the language, just
because it seems to be easy to do for the current implementation
is never a good recipe. Those 'easy' things _will_ come back to
haunt you when the implementation evolves. A seemingly innocent
feature may turn out to be an expensive burden in the future.

--Mike