Why is "Self Time" so high in an effectively empty function? - javascript

I have a computationally heavy function which is called many times in a loop:
function func() { // Some fluff
for(let i = 0; i < 1000; i++) {
i *= 10
i /= 10
}
}
function run() {
for(let i = 0; i < 100000; i++) {
func()
}
}
run()
When I profile this script using Chrome's DevTools, I get this:
run has a self time of 887ms out of the total time of 1015ms even though the only thing it does is repeatedly call func.
I would expect func to have the majority of self time since it's a leaf function.
Why is this?

(V8 developer here.)
the function was automatically inlined after some time when it became "hot".
Correct. As soon as run is optimized, the optimizer decides to inline func into it. After that, as far as the profiler is concerned, all the time is spent in run.
(To verify this, run the snippet in d8 or node with --trace-turbo-inlining.)
Side note: getting to optimized code for run takes a bit longer than usual in this case, because the function never returns to get called again (which is the best time to switch to optimized code). The system waits a bit for that to happen, and when it doesn't happen, run is eventually "on-stack replaced". This is a typical pattern that occurs frequently in small tests and benchmarks, and rarely in real-world code.
Doesn't this just show that performing 100000 function calls are more expensive than a 1000 iterations of two simple arithmetic operations -- which makes sense?
No, it doesn't show that; that's just one particular way how one could be misled by this microbenchmark.
Personally, I'm mildly disappointed to see (with --print-opt-code) that the compiler didn't realize that i *= 10; i /= 10; is a no-op and could be dropped entirely. That would have been another great way to be misled here. Oh well, there's probably some reason why it's harder than it looks for the compiler to figure out that that transformation would be both applicable and safe...

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.

Trying to Understand Javascript Closures + Memory Leaks

I've been reading up a lot on closures in Javascript. I come from a more traditional (C, C++, etc) background and understand call stacks and such, but I am having troubles with memory usage in Javascript. Here's a (simplified) test case I set up:
function updateLater(){
console.log('timer update');
var params = new Object();
for(var y=0; y<1000000; y++){
params[y] = {'test':y};
}
}
Alternatively, I've also tried using a closure:
function updateLaterClosure(){
return (function(){
console.log('timer update');
var params = new Object()
for(var y=0; y<1000000; y++)
{
params[y] = {'test':y};
}
});
}
Then, I set an interval to run the function...
setInterval(updateLater, 5000); // or var c = updateLaterClosure(); setInterval(c,5000);
The first time the timer runs, the Memory Usage jumps form 50MB to 75MB (according to Chrome's Task Manager). The second time it goes above 100MB. Occasionally it drops back down a little, but never below 75MB.
Check it out yourself: https://local.phazm.com:4435/Streamified/extension/branches/lib/test.html
Clearly, params is not being fully garbage collected, because the memory from the first timer call is not being freed... yet, neither is it adding 25MB of memory on EACH call, so it is not as if the garbage collection is NEVER happening... it almost seems as though one instance of "params" is always being kept around. I've tried setting up a sub-closure and other things... no dice.
What is MOST disturbing, though, is that the memory usage trends upwards. It might "just" be 75MB for now, but leave it running for long enough (overnight) and it'll get to 500 MB.
Ideas?
Thanks!
Allocating 25mb causes a GC to happen. This GC cleans up the last instance but of course not the current. So you always have one instance around.
GC does not happen when the program is idle. It does not happen between your timer calls so the memory stays around.
That is not even a closure. A closure is when you return something from a function, like an array, function, object or anything that can contain references, and it carries with it all the local members of that function.
what you have there is just a case of a very long loop that is building a very big object. and maybe your memory does not get reclaimed as fast as you are building the huge objects.

Can I use setTimeout to create a cheap infinite loop?

var recurse = function(steps, data, delay) {
if(steps == 0) {
console.log(data.length)
} else {
setTimeout(function(){
recurse(steps - 1, data, delay);
}, delay);
}
};
var myData = "abc";
recurse(8000, myData, 1);
What troubles me with this code is that I'm passing a string on 8000 times. Does this result in any kind of memory problem?
Also, If I run this code with node.js, it prints immediately, which is not what I would expect.
If you're worried about the string being copied 8,000 times, don't be, there's only one copy of the string; what gets passed around is a reference.
The bigger question is whether the object created when you call a function (called the "variable binding object" of the "execution context") is retained, because you're creating a closure, and which has a reference to the variable object for the context and thus keeps it in memory as long as the closure is still referenced somewhere.
And the answer is: Yes, but only until the timer fires, because once it does nothing is referencing the closure anymore and so the garbage collector can reclaim them both. So you won't have 8,000 of them outstanding, just one or two. Of course, when and how the GC runs is up to the implementation.
Curiously, just earlier today we had another question on a very similar topic; see my answer there as well.
It prints immediately because the program executes "immediately". On my Intel i5 machine, the whole operation takes 0.07s, according to time node test.js.
For the memory problems, and wether this is a "cheap infinite loop", you'll just have to experiment and measure.
If you want to create an asynchronous loop in node, you could use process.nextTick. It will be faster than setTimeout(func, 1).
In general Javascript does not support tail call optimization, so writing recursive code normally runs the risk of causing a stack overflow. If you use setTimeout like this, it effectively resets the call stack, so stack overflow is no longer a problem.
Performance will be the problem though, as each call to setTimeout generally takes a fair bit of time (around 10 ms), even if you set delay to 0.
The '1' is 1 millisecond. It might as well be a for loop. 1 second is 1000. I recently wrote something similar checking on the progress of a batch of processes on the back end and set a delay of 500. Older browsers wouldn't see any real difference between 1 and about 15ms if I remember correctly. I think V8 might actually process faster than that.
I don't think garbage collection will be happening to any of the functions until the last iteration is complete but these newer generations of JS JIT compilers are a lot smarter than the ones I know more about so it's possible they'll see that nothing is really going on after the timeout and pull those params from memory.
Regardless, even if memory is reserved for every instance of those parameters, it would take a lot more than 8000 iterations to cause a problem.
One way to safeguard against potential problems with more memory intensive parameters is if you pass in an object with the params you want. Then I believe the params will just be a reference to a set place in memory.
So something like:
var recurseParams ={ steps:8000, data:"abc", delay:100 } //outside of the function
//define the function
recurse(recurseParams);
//Then inside the function reference like this:
recurseParams.steps--

Javascript Optimization: Looping and handling different ways in different browsers

In my quest to optimize my game engine I have discovered optimization i have been doing affecting each browser differently, in a lot of cases making one browser worse and the other better!
Currently I'm trying to optimize looping as i do alot of it and depending on the way this is done can have a big effect on the performance of my engine.
Based on the results here http://jsperf.com/for-vs-while-loop-iterating/3
It seems a reverse for loop in chrome is the fastest
var l = foo.length;
for (var i = l; i--;) {
}
And in firefox a forward for loop is fastest
var l = foo.length;
for (var i = 0; i < l; i++) {
}
Now in order to use the correct one per browser I'm doing something like this
function foreach(func, iterations){
var browser = $.browser;
var i;
if (browser.webkit)
{
for(i=iterations;i--;)
{
func(i);
}
}
else
{
for (i = 0; i < iterations; i++)
{
func(i);
}
}
}
but it seems there may be alot of overhead here that may hurt performance.
If you were to provide different ways of looping for different browsers what would you do?
EDIT: seems there was a bug in my testing where i was doing one too many loops on the forward loop and now chrome seems to be the fastest there also, I may not need to optimize the loops but it may still be worth while as mention in another comment incase browser versions change performance again
Unfortunately, if your goal is the best performance loop on each browser, the very last thing you want to do is introduce function calls into it. There's no way you can define your foreach function such that it will be anything like as fast as the straight for loop. Calling the iteration function will wash out any gains you might get.
A quick jsperf can confirm this easily enough. For instance, run this one on Chrome or a recent version of Firefox or Opera. It compares looping forward with for, backward with for, or using the browser's built-in Array#forEach function (part of ECMAScript5). (I think we can assume any foreach function you build will be slower than the built-in one.) As you can see, the forEach version is dramatically slower than either of the for loops, which makes sense: Calling functions isn't very expensive, but it isn't free.
Mind you, what you're doing in the loop probably washes out the difference between counting up and count down. What I'd do is figure out what's fastest on the slower browsers, and use that. You said, for instance, that a reverse loop is faster in Chrome but a forward loop is faster in Firefox. As Chrome's V8 is dramatically faster than Firefox's SpiderMonkey (at the moment, these things are constantly in flux), pick the forward loop, as it's faster on the slower engine.
Otherwise, you're into needing to do preprocessing on your code and churning out a different version tailored to each browser.
I don't think your overhad is as big as you feel, but what you can do is do your test only once:
var foreach;
if (forwardIsFaster) {
foreach = function (func, iterations) {
// loop forwards...
};
} else {
foreach = function (func, iterations) {
// loop backwards...
};
}
That said, I'm not sure using browser sniffing is the best solution here; maybe instead do a test loop on startup, measure which solution is faster, and choose the one that turns out to be faster.
When you start doing something significant inside the loop (e.g. just printing the variable), the order of iteration doesn't matter, see:
http://jsperf.com/for-vs-while-loop-iterating/4
So stop caring about this and if (and only if) your code is slow in any place, just profile it and optimize that part.
You could make foreach return a function, which would be an iterator. So you would have, once in your script, var iterator = foreach($.browser.webkit);. From then on, you would just use iterator(iterations, callback), which would no longer be executing any conditionals.
The key, basically, is that the user's browser won't change, so the result of the execution of that conditional needs to be evaluated only once per script.

Categories