A lesson on structs, recursion, stack size and pointers.

Dan murpsoft at hotmail.com
Sun Jan 20 16:37:08 PST 2008


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.

So....

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.


More information about the Digitalmars-d-learn mailing list