Why is recursion faster than iteration in JS? [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I've been doing JS problems on LeetCode and noticed that the recursive solutions run faster than the iterative solutions in almost every case (ex. DFS with a stack vs recursion).
I looked online and found a couple of blog posts that came to similar conclusions around performance.
https://www.c-sharpcorner.com/blogs/performance-of-recursion-vs-loop-using-javascript
The Oreily book, like most formal material on the subject, states that iteration is more performant because of the "lower overhead" which I understand but then why isn't that the case in practice?
https://www.oreilly.com/library/view/high-performance-javascript/9781449382308/ch04.html
I do see that recursion can fail at particularly large stack sizes but it always seems to be more performant than iteration. What's going on? I saw that TCO hasn't been implemented in JS so I'm very confused.

Well, to understand recursion over iteration (or vice versa), we need to understand how both technique works.
What is recursion?
It's function calling it self. so number of calls take place, number of times the properties of function (which includes variables, data-structures, sub-functions that the main function uses, will recreate over and over again). and at the end, compiler or (JIT in case of JavaScript) must face some overheads.
And the process will continue until/unless base case have found, in this case all overheads during recursion will release.
In case, if base-case not found/matched all the stack-frame which was allocated for this particular function will be exhausted, and we will get some thing like: Maximum call stack size exceeded
What is Iteration?
As the name suggest, it's a repetitive instruction which will perform some operation given by the coder.
In compare to Recursion, no other stack-frame will be used without main-stack-frame. and all the properties of the function will be reinitialised with new value over and over again.
Based on the comparison i have made, you can clearly get an idea, which is more and which is less efficient to Compiler or to JIT. (and it is generalised to every language you will work on).
Now coming to the performance, the article link which you provided clearly not showing accurate measurements of all the scenarios. The time to execute a function clearly and mainly depends on the number of instruction the function performs.
Looking at the following snippets you can re-think about the conclusion you have so far:
function nthFibo(a){
if(a <= 2){
return a;
}else{
return nthFibo(a-1)+nthFibo(a-2);
}
}
console.time("looping #1");
console.log(nthFibo(45));
console.timeEnd("looping #1");
and also check the time required for iteration, which will give you same output as recursion gave:
function nthFiboIterative(a){
var first = 0, second = 1;
var result = 0;
var i = 1;
while(i ++ <= a){
result = first + second;
first = second;
second = result;
}
return result;
}
console.time("looping #2");
console.log(nthFiboIterative(45));
console.timeEnd("looping #2");
But this is not a fair test as our first program creates an exponential process that we compare to a linear process in the second program. Let's see a recursive nthFibo that evolves a linear iterative process -
function nthFibo (a, x = 1, y = 1)
{ if (a == 0)
return x;
else
return nthFibo(a - 1, y, x + y)
}
console.time("looping #3");
console.log(nthFibo(45));
console.timeEnd("looping #3");
It's important to know the distinction between process and a procedure (function). A recursive function can evolve a recursive process, an exponential process, or an iterative process. Similarly, a non-recursive function can evolve an iterative process (think simple for loop) or an exponential process (think something like Ackerman function).

Related

try/catch - How many recursive calls? call stack size loop

I have this function which I want to loop without stopping the script.
But when I use 'console.log' the function stops. If I remove it, the function continues.
How to make this function continue after it has exceeded stack size?
var i = 0;
function computeMaxCallStackSize() {
try {
i++;
console.log(i);
//some function to execute
computeMaxCallStackSize();
} catch (e) {
//'e' is the error when max stack exceeds
// setTimeout() is to wait before calling the function
// if stack size exceeds it recalls the function
setTimeout(computeMaxCallStackSize(), 0);
}
}
computeMaxCallStackSize();
Edit:
This function will cause a stack overflow and continues.
var a = 1;
var b = 8769; //stack size
func(a);
function func(a) {
try {
console.log(a);
if(a % b === 0){
setTimeout( function() {func(a+1); }, 0);
} else {
func(a+1);
}
} catch (e) {
console.error(e);
setTimeout( function() {func(a); }, 1);
}
}
If I remove it, the function continues.
No it doesn't, it will finish also, but much later. console.log() taking much more resources cause it needs to render text in console, so it's reaching call stack faster.
You just made infinite loop, that will run till browser will run out of memory.
I don't see a way to catch stackoverflow in nodejs. From what I know from other ecosystems, it is impossible. See C# catch a stack overflow exception for example. Also see a note about exceptions:
Some exceptions are unrecoverable at the JavaScript layer. Such
exceptions will always cause the Node.js process to crash. Examples
include assert() checks or abort() calls in the C++ layer.
Process stack is fixed-size, contiguous block of memory, usually not very big (because it's purpose is to make use of process caches). This means that you can't "extend" it on your wish (only at process start). If you can't extend it, why bothering checking it's boundaries every time you do a function call? OS will do the job and crash your process for you!
I know it's not always possible in case of highly-recursive academic algorithms, but from business perpective, you should always unwrap your recursion, especially if it's deep. Even if you think you control it (like, it's only 2000 deep, who cares?), in reality you don't, since usually you don't fully control the caller stack size, and the end-user platform limitations.
BUT!
If you REALLY want to recover your process after stackoverflow, and you're ready for such an adventure (win api, build node from sources, etc.), I will leave this for you.
Your setTimeout is kind of pointless the way it is:
// Current code:
setTimeout(computeMaxCallStackSize(), 0);
// What you really want:
setTimeout(computeMaxCallStackSize, 0);
To answer your question - you don't want to write code which can exceed the stack - that's always an indication of poorly written code. Furthermore, there's not really a way to check the stack size or to check when the stack is "clear" again (for good reason - people would abuse that knowledge to write poor code).
If you need recursion, you almost always build in a "depth" parameter to the function so the function stops after a certain call depth. Something like this:
function someRecursiveFunction(_depth = 0) {
if(_depth < 100) {
// do the stuff
someRecursiveFunction(_depth + 1)
}
throw new Error('Oh no, we called this function too many times');
}
If you're just doing this for giggles and really want to see how deep the stack hole goes, I guess you could count the iterations. As mentioned in other comments, the console log is going to take up additional resources - so you might get 1 million iterations without the logging and only 100K with logging. Again, there's no way to ask the JS runtime "how many resources to I have left". The function you are writing is simply a "for fun" experiment and should not be used in real life code.

Javascript recursive function performance degradation

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.
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.
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.

Java Script Variable Problems [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
Is there a way to null out all variables or delete any data inside them. Beside writing
Variable1=null;Variable2=null;
...so on and so forth. Please no software.
It's not that it's taking much memory, I just want it to load fast. Why I want to null the variable is because I want no mistakes even possible. The code looks like this:
var First_variable;
for (var I_am_a_loop=0;I_am_a_loop<10;--I_am_a_loop){
First_variable=prompt("What is your name.");
}
Now this is the just but it will have arrays and be more advanced. Why I want to null the variable is because I want no mistakes. I don't have the script complete so I cant give you a whole example.
If you are reassigning the variables in the loop setting them to null for a few milliseconds isn't really going to solve any problems for you.
Even if you do declare them within the loop*, modern JavaScript garbage collectors shouldn't have any trouble getting rid of unreferenced variables. The only time you would expect there to be a problem here is with circular references (so the variables always have a reference) or very fast creation of lots of large objects, which causes a lot of garbage handling.
if you use var they are hoisted to the top of the function, if you use let they will have block scope (ECMAScript 6 only).
Regarding your comment:
its not that its taking much memory it is that i want it to make sure it will not mess up it anyway
May I be the first to quote the Donald Knuth...
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
I recommend you measure and take no action until you have a measurement that demands it. Write your code to be readable, not efficient, by default.
Code
Based on the code you added, I'd make some observations... you seem to be decrementing the loop variable, so it is going to prompt forever. You can do that if you want, but it is easier to just use a while(true) {} loop to make it clear you want to do it.
Otherwise, if you really intend to do the loop 10 times, increment the loop variable instead.
var First_variable;
for (var I_am_a_loop = 0; I_am_a_loop < 10; I_am_a_loop++) {
First_variable=prompt("What is your name.");
}
Even if the user enters nothing, the value of First_variable will be overwritten. You can always supply a default for when they don't enter a value:
var First_variable;
for (var I_am_a_loop = 0; I_am_a_loop < 10; I_am_a_loop++) {
First_variable = prompt("What is your name.") || 'Default Name';
alert(First_variable);
}
Seems like an odd question, but to answer. You could wrap the relevant code in a function and rely on javascript's function-level scoping to give you fresh instances with each call of the function.
You might be able to refactor your code such that you are using an object or an array in lieu of direct variables.
You could somewhat shorten your example code by one chained assignment statement, such as:
variable1 = variable2 = null; // saves you a bunch of '=null;' if nothing else
My general thoughts are that if you have fewer than 10 variables you need to reset, it's a minor complaint. If it is many more than 10 variables you need to reset, you probably want to look at your code.

Error traversing large arrays in JavaScript without timeout

When I'm am executing the following code in Node.js, I get the folliowing error:
RangeError: Maximum call stack size exceeded
This is the code:
var arr = [];
for (var i = 0; i <= 1000000; i++)
arr.push(i);
function nextStep(index) {
if (index === arr.length) {
return;
} else {
console.log(index);
}
nextStep(++index);
}
nextStep(0);
I have no clue what is happening, but near index = 17938, the execution terminates.
Using a setTimeout() helps. What could be wrong here?
You are entering a recursive function. This means that the first function won't return until all the other functions have returned. So if you had four items,
fn(item1)
calls -> fn(item2)
calls -> fn(item3)
calls -> fn(item4)
As you can see, the nesting builds up and up. This is called the stack. There is a maximum size for the stack, to prevent infinite recursion and runaway processes. You have found it with 17938.
This is an inherent flaw with recursion. It can be a stylish way to approach a task, but it has its limitations. The best way to correct it is to use a loop instead:
for (var i = 0; i < arr.length; i++) {
Using setTimeout also works, because the function is not called from the function itself, but is executed with a new stack. However, it will have significantly lower performance than a loop or than a normal recursive function.
There's a certain number of calls that can be made, based on the call stack of different browsers. Most likely, you are testing your code in Chrome, since, I believe, it has the call stack near 20.000. Your code will execute the nextStep function more than 20.000 times (1 million), which means that if your function will not return something until it reaches the call stack limit of that particular browser, it will generate the error that you are getting.
Calling nextStep inside nextStep causes a stack overflow since you never return from the function (unless you found the end of the array, and if the array is too big you will never reach it until the stack overflows).
An example:
You are going to move all the stones from one place to another. Your function is like going to the place where the stones are and pick up one to be delivered to another place. But before you can deliver the stone, you must pick up another, and before you can deliver that you need to pick up another... and soon you are carrying 17938 stones. That is a little bit to heavy and you get crushed under all the stones. (or in the javascript case you get an exception)
When you use setTimetout it is like you are going to the place, pick up a stone, and make a note that you should pickup another stone. Then you deliver the stone. After that you look at your notes and see that you should pick up at stone. And you can do this 1000000 times since you are only carrying one stone at a time.

What is the Definition of Recursion

So, we all know recursive functions, right? But what exactly is it, that makes a function recursive? I had a small discussion in the comment section of another question (Lightening effect with javascript) that kind of challenged my view of recursive functions but it also left me with a quite unsatisfying lack of proper definition.
My personal definition of recursive functions so far was the following:
A function is recursive if it directly or indirectly invokes itself
Note: I will provide JavaScript code for the following examples, but I'm sure they are pretty universal.
A simple example for such a recursive function could be this:
function a() {
a();
}
but also this:
function a() {
b();
}
function b() {
a();
}
or even this:
function a() {
setTimeout(a, 1000);
}
None of those functions computes anything but I would have still considered them recursive just because they invoke themselves.
One thing that came up was, that the third example is not recursive because it uses setTimeout and therefor the stack is unwound. It also isn't recursive because after returning from the nth invocation it doesn't give back control to the n-1th invocation.
Another point, that was raised, was that none of those functions were recursive because they all don't compute an actual problem in a recursive way. Meaning that a problem has to be solved by dividing it in smaller and smaller instances. Here the quoted Wikipedia article:
Recursion in computer programming is exemplified when a function is
defined in terms of simpler, often smaller versions of itself. The
solution to the problem is then devised by combining the solutions
obtained from the simpler versions of the problem.
So this is recursive:
function fac(n) {
if (n <= 0) {
return 1;
}
return n * fac(n - 1);
}
But this wouldn't be:
function fac(n) {
if (n <= 0) {
return 1;
}
console.log(n);
fac(n - 1);
}
So what is the proper definition of a recursive function? Is there any at all or is it really just a philosophical question? What features must a function have, in order to be classified recursive?
Recursion is simply defining a problem in terms of a simpler case (simpler meaning "closer" to the terminating condition, not necessarily actually simpler) of that problem, down to the point where the simplest case is a known one (the terminating condition alluded to earlier). So, for example, the perennial factorial function has a terminating condition:
f(1) = 1
and the definition of the problem in terms of a simpler one:
f(n) = n * f(n - 1), for n > 1
The best explanation of it that I ever heard was this:
If you're Donald Knuth, you understand what it is.
Otherwise, find someone closer to Donald and ask them.
I wouldn't call the setTimeout one recursion because a is not actually calling itself. Instead, it's asking the "system" to call it at some later date.
It's also important to have the terminating condition in there somewhere. Without that, it's still recursion but it's infinite recursion, no different to infinite loops like:
for (i = 0; i < 10; j++) {}
and hence unlikely to be any good for anything other than testing what happens when your stack overflows :-)
Definition of Recursion? Read this line again until you get it.
(A selfcalling function with an abort criterium to prevent infinite looping).
Recursion is the name that was given to function that calls itself. Now whether the function calls itself infinitely or not.. it is still a Recursion.
It is not necessarily that the problem is divided into sub-problems. However, in computer science; The term Recursion refers to a technique that is used to solve a problem, by breaking the problem into sub-problems and usually the problem is finite.
One more point, Recursion is implemented using Stack. Each function call is piled on top of the other in the stack, until the last call meets the base condition, then the functions in the stack are executed from top to bottom.
However, if there is no base condition or the base condition is never to be satisfied. then infinite calls to the function will be pushed to the stack causing the memory to be filled up and a stackOverFlow exception will be thrown and OS handles it by terminating the program.
In Regard to setTimeout() It is an asynchronous call and is not related to recursion, it is an independent call as the caller function doesn't depend on the called one whether it is the same function being called or another.
From Wikipedia that you have posted:
Recursion in computer programming is exemplified when a function is defined in terms of simpler, often smaller versions of itself. The solution to the problem is then devised by combining the solutions obtained from the simpler versions of the problem.
So. There is a problem, and there is a solution. There is a function that call itself minimizing the main problem. This function is recursive.
Case:
function a() {
a();
}
there is no problem, there is nothing to minimize, there is no solution. It's not recursive for me. It's just an infinite loop.
So another example:
function a(n) {
if(n<.5) {
return n+a(Math.random());
}else {
return n;
}
}
console.log(a(.3));
Is this recursive?
No.
There is maybe a problem, but the solution is not found minimzing the main problem. Simple it call itself randomly until some flag is true. Again, it's a loop.
The same happens with a setTimeout or setInterval (the solution of problem will depend from system call).

Categories