[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Casual object churn and efficiency.
- From: Mark Hamburg <mhamburg@...>
- Date: Fri, 25 Jun 2004 07:34:35 -0700
Someone with experience can use this as an opportunity to plug luarc.
If modifying Lua, approaches could also be taken to implementing
generational garbage collection. For example, it might be useful to
segregate objects into objects that have only been referenced from the stack
and objects for which there have been other references.
All of those are changes to the Lua implementation and while they cut down
on the time spent doing GC work don't avoid trips through malloc and free.
Preserving your semantics about vectors, one approach is to try to manage
the temporaries by hand. You can do this without destroying the simple API,
while enabling hand optimization in loops.
For example:
local function vector3_add(self, o, rtn)
rtn = rtn or self();
for i=1,3 do
rtn[i] = self[i] + o[i];
end
return rtn;
end
Etc.
Note that rtn is optional as a parameter. If it's nil, we will create a new
one.
Then you could write in a loop (with appropriate method extensions):
result = vector1:add( vector2, tmp1 )
:scale( 0.5, tmp2 )
:cross( vector3, tmp3 )
:normalize()
No temporary on normalize under the assumption that we need to save the
value. This example actually uses too many temporaries.
Or one shifts to an API that is based around changing the target object
rather than creating new objects:
result = vector1:copy()
:add( vector2 )
:scale( 0.5 )
:cross( vector3 )
:normalize()
Finally, if one wanted to get back to the pure algebraic API, then one could
use the following mechanism inspired by Cocoa's autorelease pools:
You maintain two lists. A list of available temporaries and a list of
temporaries currently in use. Allocation comes from the first list and adds
them to the second list. If the first list is empty, then you just do a new
allocation. There are then two additional operations that you need to add:
retain: This removes a value from the in use temporary list. It's what you
call before storing a value for an extended time. It effectively turns a
temporary into a non-temporary.
recycle: This takes all of the in use temporaries and puts them back in the
list of available temporaries. You need to be careful not to have anything
in the list still in use at the time.
A more elaborate scheme along the lines of Cocoa would be to have a series
of pools of in use temporaries.
Mark
on 6/25/04 7:17 AM, Adam D. Moss at adam@gimp.org wrote:
> Hi!
> Encouraged by Lua's metamethods, I find myself cheerfully
> doing things like:
>
> result = ((vector1 + vector2) / 2):cross(vector3):normalize();
>
> While this is wonderfully convenient, a woeful downside of
> this is the number of extremely short-lived objects that get
> created along the way. One of my object-heavy apps (a mesh
> simplifier) thus profiles as spending 20-40% of its time
> solely in garbage collection (lua 5.0/5.1).
>
> To support the natural syntax above, my Vector3 object's
> metamethods return a newly-instantiated object of the same
> type, i.e.:
>
> local function vector3_add(self, o)
> local rtn = self();
> for i=1,3 do
> rtn[i] = self[i] + o[i];
> end
> return rtn;
> end
>
> The question is, can anyone think of a way to either:
> * Alter my object usage and design patterns to create fewer
> short-lived objects while not overly killing convenience
> * Fairly easily 'tweak' Lua to optimize for the very-short-lived
> object case
>
> (Would the latter be one of the things traditionally covered
> efficiently by a 'generational' garbage collector?)
>
> Thanks,
> --Adam