> I implemented a scheme for recording the maximum depth of the C stack in
> xmalloc and during garbage collection itself. However, I realized that
> there was no point in clearing the stack when it is near its maximum depth.
> Instead, stack clearing is deferred until CHECK_INTS, as this tends to
> happen
> between evaluation of nodes, when the stack is likely to be shallower.
>
> At this point
> a tight loop quickly zeros the region between the current top of stack, as
> returned by alloca(0), and the maximum recorded stack extent. It also
> updates
> the stack extent so no memory is cleared repeatedly if the stack contracts
> further.
This is sweet. I liked the idea so much I coded my own [perhaps much
smaller, definitely less effective] version. It only includes the
stack clearing you referred to, and doesn't even monitor "exactly" the
stack size, but approximates it by metering it once every CHECK_INTS.
Ruby seems to run "as fast as normal" with it, and collect better.
In principle, you'd only have to clear the stack once "between each
GC" so if you kept track of which portions of it you'd been able to
clear, you could avoid a few stack clearings :)
I'm not sure exactly how much cpu that would save, though.
This patch also doesn't fix the
loop {@x=callcc{|c|c}}
aspect [presumably because ruby's green threads copy chunks of the
stack to heap, so they aren't cleaned]--so I'd imagine it's less
effective in multi-threaded codes [but hopefully still helpful].
Look forward to the real patch when it comes in :)
Note that as it is currently, if you run GC.start it also calls
clean_stack, so if you run GC.start when your program is at it "inner
depth [most nested call]" it will notice exactly how deep it is, and
hopefully clean up the stack "all the way" when you ascend out of deep
calls. I suppose creating a new call "GC.clear_stack" would be
useful.
i.e. GC.start -> GC.start + "clean stack/make a note of how deep the
stack is currently"
With [1] it successfully prevents the string 'a' from not being
garbage collected:
With [2] it successfully collects a few more objects than the unpatched does.
I'm not positive how well it works but I think it does.
Enjoy.
-=R
[0] patch: http://wilkboardonline.com/roger/clear_stack_only2.diff
[1] file.rb:
def does_nothing
end
def deep(how, gc = false)
if(how == 175)
'a'*1000
end
if how == 300
print "222222deepest"
GC.start
print "222222deepest"
return
end
deep(how+1)
20.times {does_nothing}
end
puts
deep(0)
GC.start
deep(0, true)
count = 0
ObjectSpace.each_object(String) do |s| print s, ' '; count = count+1; end
print count
[2] file2.rb:
count = 0
ObjectSpace.each_object{|o| count += 1 }
print count
GC.disable
def go depth
if depth == 50
GC.enable
GC.start
return
end
if(rand(10) == 3)
a = 'abcd'
go(depth+1)
go(depth+1)
end
if(rand(10) == 3)
b = 'abcd'
end
go(depth+1)
end
go 0
count = 0
ObjectSpace.each_object{|o| count += 1 }
print count