How to Increase Stack Size For a Ruby App. Recursive App Getting: Stack Level Too Deep (Systemstackerror)

How to increase stack size for a ruby app. Recursive app getting: Stack level too deep (SystemStackError)

Ruby uses the C stack so your options include using ulimit or compiling Ruby with some compiler/linker stack size flag. Tail recursion is yet to be implemented and Ruby's current support for recursion isn't so great. As cool and elegant recursion is, you might want to consider coping with the language's limitations and writing your code in a different way.

Is it possible to create SystemStackError: stack level too deep errors without recursion?

In this example, does it detect the certainty of running out of stack or does it actually run out of stack?

It actually runs out of stack. It is, in fact, impossible to "detect the certainty of running out of stack" in the general case because of the halting problem, one of the core problems in computer science.

Is it possible to produce this error without using recursion?

Sure. Just define a lot of methods, each of which calls the next:

20000.times do |n|
define_method :"foo_#{n}" do
puts "Called #{__method__}"
send :"foo_#{n+1}"
end
end

foo_0
# -> Called foo_0
# -> Called foo_1
# -> Called foo_2
# -> Called foo_3
# ...
# -> Called foo_4931
# -> SystemStackError: stack level too deep

Ruby stack level too deep exception NOT from recursive infinite loop

Are there other cases that raise stack level too deep exception, besides the classical program-execution-tree-is-too-deep scenario?

Yes. Since the stack is not measured in depth, but in bytes, anything stored on the stack will fill it up sooner:

def recurse(depth=0)
recurse depth+1
rescue SystemStackError
depth
end
=> nil
recurse
=> 8717

def recurse(depth=0)
a,b,c = 1,2,3
recurse depth+1
rescue SystemStackError
depth
end
=> nil
recurse
=> 7264

def recurse(depth=0)
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z = *(0..25)
recurse depth+1
rescue SystemStackError
depth
end
=> nil
recurse
=> 3187

In this example, a function with only one variable can go several thousand calls deep before failing, and adding three more variables does very little; but adding 26 more variables balloons the stack size to a point where only around 3000 levels are available.

This will, of course depend somewhat on the ruby implementation, and the system it's running on. But I believe it will always hold as a general rule.

However, I still think recursion is likely your problem, since the number of variables required to have this happen at small call chain lengths is immense.

Is it possible to create SystemStackError: stack level too deep errors without recursion?

In this example, does it detect the certainty of running out of stack or does it actually run out of stack?

It actually runs out of stack. It is, in fact, impossible to "detect the certainty of running out of stack" in the general case because of the halting problem, one of the core problems in computer science.

Is it possible to produce this error without using recursion?

Sure. Just define a lot of methods, each of which calls the next:

20000.times do |n|
define_method :"foo_#{n}" do
puts "Called #{__method__}"
send :"foo_#{n+1}"
end
end

foo_0
# -> Called foo_0
# -> Called foo_1
# -> Called foo_2
# -> Called foo_3
# ...
# -> Called foo_4931
# -> SystemStackError: stack level too deep

Is there a workaround for stack level too deep errors in recursive routines?

If you're using YARV (the C based implementation of Ruby 1.9), you can tell the Ruby VM to turn tail call optimization on:

RubyVM::InstructionSequence.compile_option = {
:tailcall_optimization => true,
:trace_instruction => false
}

def countUpTo(current, final)
puts current
return nil if current == final
countUpTo(current+1, final)
end

countUpTo(1, 10_000)

Ruby profiler stack level too deep error

One workaround would be to turn tail call optimization on.

The following is an example of something that works with TCO on, but doesn't work when TCO is off.

RubyVM::InstructionSequence.compile_option = {
:tailcall_optimization => true,
:trace_instruction => false
}

def countUpTo(current, final)
puts current
return nil if current == final
countUpTo(current+1, final)
end

countUpTo(1, 10_000)


Related Topics



Leave a reply



Submit