www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - A lesson on structs, recursion, stack size and pointers.

reply Dan <murpsoft hotmail.com> writes:
I suppose this isn't limited to D, but I thought I'd write it here anyways,
'cause I just figured this out while writing Walnut.

Since I'm writing a scripting engine, most of the methods have to have the same
function signature so they can be interchangeable (don't ask, I don't know why).

We have a struct, Value, which is 16 bytes on a 32-bit machine.

So the function signature I went with originally was this:

static Value myFunc(Value self, Value cc, Value[] arguments ...){}

Recently while reverse engineering the resulting code in IDA Pro, I figured out
that this was spitting out a rather atrocious 48-byte stack signature for each
call, and to optimize things, D was performing a rep movsd to get the
parameters accross; and because this wasn't trivial code, it had separated it
out and was calling this algorithm from each of my methods to copy the
arguments accross.

My first thought was to use SSE2 registers to pass the Value structs in, but I
realized that for recursion, you still need to put it on the stack, so the idea
doesn't help.


static Value* myFunc(ref Value self, ref Value cc, arguments[] ...){}

apparently consumes far less.  The problem I'm now facing is that since I'm
always returning pointers I need to allocate Value's somewhere other than the
constructor's call stack.

My first thought was to write a block allocator, since everything is being
handled by Value struct.  I thought that doing so could take a great deal of
load off the GC.

Bad idea.

I figured out that because it was originally on the stack, the GC wasn't being
stressed by it anyways.  When I was passing by Value, as soon as it went out of
scope, it was automatically crushed as a local variable.

So now, having a pointer, I needed to allocate it somewhere outside the stack,
and was facing the memory management problem I think I was originally trying to
avoid at some point when I had actually thought this through.  I need Values to
have local scope to the function calling the constructor on it; but that
function often doesn't exist at compile time.

So I kind of wanted to fake a closure?

The real answer is that D's GC already cleans up if nothing points to it.  So,
the pointer on the stack gets cleared, and later the GC comes along and digests
the Value struct itself.
Jan 20 2008
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Dan" <murpsoft hotmail.com> wrote in message 
news:fn0pfk$kup$1 digitalmars.com...

 static Value* myFunc(ref Value self, ref Value cc, arguments[] ...){}
Your other issues aside, have you considered using/passing some kind of context which would hold all these values instead? In MiniD (which uses an API heavily inspired by Lua), native functions take two params -- a class reference to the current "thread", and a number representing the number of params passed to this function. The function then asks the thread object for any params it needs, since they're all on the thread's stack anyway. To return values, the native function pushes all the values it wants to return on the thread's stack and returns a number indicating how many values to return (functions can return any number of values). I don't know anything about the internals of Walnut, and therefore if this scheme would be possible for you, but it sure is faster to pass a pointer and an int. It also solves the return value allocation issue.
Jan 20 2008