3

I'm hunting down a problem in a recursive algorithm I wrote.

This algorithm would throw a RangeError: Maximum call stack size exceeded (in Chrome) Error on some inputs. But the call stack I tracked down was only about 6k-9k in size.

This test (from this SO answer) indicates a maximum call stack size of about 42k in Chrome.


After running some tests, I found, that having arguments on the recursive functions seems to lower the available call stack size:

With arguments: call stack size exceeded on ~31k (Chrome, ~15k on Edge)

var recursionA = function(a, b) {
    count++;
  if (count < 100000) {
    recursionA(a, b);
  }
}

Without arguments: call stack size exceeded on ~42k (Chrome, ~16.5k on Edge)

var recursionB = function() {
    count++;
  if (count < 100000) {
    recursionB();
  }
}

See fiddle here

  1. Can anyone explain, why the available call stack size is significantly lower, when the function is called with arguments.
  2. Since my recursive function would require 2 arguments: How can I utilize the max call stack size of the browser?
  3. Are there other factors that can potentially reduce the available call stack size?
Community
  • 1
  • 1
jHilscher
  • 1,810
  • 2
  • 25
  • 29

2 Answers2

2
  1. The size of the stack is some number of bytes, not some number of function calls. Every parameter you add to a function call consumes some memory, so less stack available;
  2. See 1 above
  3. The variables in the function called
Ilya
  • 5,377
  • 2
  • 18
  • 33
2

I would just make another recursive function to call that recursive function and split up the duration so you dont max out the stack. Here is an example of a "callStackOptimizer" that I just wrote for a wayy over complicated and expensive fizzbuzz game written in a more functional immutable style to show you what I mean.

"use strict";

const isDivisibleBy = divisor => number => number % divisor === 0;

const isDivisibleBy3 = isDivisibleBy(3);
const isDivisibleBy5 = isDivisibleBy(5);
const isDivisibleBy3And5 = number => isDivisibleBy5(number) && isDivisibleBy3(number);

const rules = (bool1, bool2, bool3) => (value1, value2, value3) => number => {
    switch (true){
        case bool1(number):
            return value1;
        break;
        case bool2(number):
            return value2;
        break;
        case bool3(number):
            return value3;
        break;
        default:
            return number;
    }
};

const gameConditions = rules(
    isDivisibleBy3And5,
    isDivisibleBy3,
    isDivisibleBy5
);

const fizzBuzzResults = gameConditions(
    "FizzBuzz",
    "Fizz",
    "Buzz"
);

const game = duration => value => (action, array = []) => {
    if (duration > 0){
        const nextValue = action(value);
        return game(duration - 1)(value + 1)(action, array.concat(nextValue))
    }
    else {
        return array
    }
};

const callStackOptimizer = (duration, times = Math.ceil(duration/10000), result = []) => rules =>{
    if (times > 0){
        const value = duration/times;
        const round = game(value)(value * times - value + 1)(rules);
        return callStackOptimizer(duration - value , times - 1, result.concat(round))(rules)
    }
    else {
        return result;
    }
};

const playFizzBuzz = duration => callStackOptimizer(duration)(fizzBuzzResults);

console.log(playFizzBuzz(100000));