9

I've been made the following question during a hiring process skills test:

var x = function(z) {
    console.log(z);    
    if (z > 0) {
        x(z-1);
    }
};

why this is progressively slower as z get higher? propose a better version, keeping it recursive.

And I want to know the answer just to learn about it. I answered that it gets slower because as z increases the number of recursive calls increases too, but I could not provide a better version. In addition, I don't know if there is any further reason why the function become slower as z get higher.

8
  • 5
    It takes more time because it logs to the console more often? It's really hard to imagine what they wanted to hear. Did you ask them? Commented Feb 16, 2016 at 16:44
  • No, I haven't been able to to ask them yet but I'll do it if I can. Commented Feb 16, 2016 at 16:46
  • 1
    If they indeed mean that this is exponentially slower for large numbers of z than for small numbers, the only real reason for that would be that functions on the top of large call stacks would somehow execute more and more slowly; that the call stack would somehow accumulate overhead. This obviously greatly depends on the Javascript implementation. Have you run any tests to confirm that this in fact does slow down? Commented Feb 16, 2016 at 16:48
  • I did some manual tests in google chrome's debugger console during the tests, but I couldn't test it deeply because I had a time limit to answer the question. I my test I saw that if call the function with a really bit value for z it gets slower than with smaller values, but I can't say the exact proportion. Commented Feb 16, 2016 at 16:56
  • 2
    Are you sure you recall an exact code snippet? I imagine a question about a recursive fibonacci function that calls itself twice thus leading to actually observable growth of recursive calls. This opens up a discussion of a possible cache, an iterative version or even a version with an accumulator and a single recursion. Commented Feb 16, 2016 at 17:01

2 Answers 2

11

The correct answer would have been, "It should not be progressively slower as z gets higher". In fact, it does not in my simple tests. My tests overflow the stack before any slowdown is visible.

I cannot imagine any alternative way to write the function while maintaining its recursive nature.

Sign up to request clarification or add additional context in comments.

1 Comment

I was brushing up on Duff's Device to see if that could be a sensible answer, but trying to do that recursively throws a Too much recursion error with x(1000). I think this may have been what the question was hinting at, but I think your answer is correct.
2

Since JavaScript doesn't have true tail calls (yet) what you are experiencing isn't a slowdown based on the value of z but a slowdown based on stack growth and the inability of the garbage collector (GC) to cleanup the stack of scopes that are necessary to retain this functionality.

In theory, you can change this behavior by using setImmediate and the callback pattern to solve for this. Using setImmediate allows the scope of the current execution loop to fall out of use and get collected during the next GC cycle.

So, something like:

var x = function x(z){
    console.log(z);    
    if (z > 0) {
        setImmediate(function(){
          x(z-1);
        });
    }
};

But this will not work because z is passed down into the scope for setImmediate and thus the previous scope of x can't be GC'd properly.

Instead you have to use IIFE (Immediately Invoked Function Expression) to achieve what you are looking for in combination with using setImmediate to allow the execution cycle to have time to allow for GC:

var x = function x(z){
    console.log(z);    
    if (z > 0) {
        setImmediate((function(newZ){
          return function(){
            x(newZ);
          };
        })(z-1));
    }
};

Barring any typos I think I got the above correct. Of course if your using ES6 you can shorthand this greatly as well.

The other side of this equation is the spooling effect of console.log and the buffer resizes your going to incur for doing this in a browser. In an OS terminal these costs will be minimized as backbuffer scroll is limited and the back buffer is pre-allocated and reused.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.