All About Recursion and Tail Calls in JavaScript
lucasfcosta.com
lucasfcosta.com
As program correctness is concerned, tail calls and recursive calls should behave exactly the same (except stack use), so going by correctness alone, either the choice doesn't matter at all or tail calls are preferable. So the logical thing to do would be to always use tail calls.
The post goes on to list a number of disadvantages, but those seem to be either debugging concerns or platform-specific implementation concerns.
The former doesn't seem to be something that should be solved in the code - the document itself lists a number of solutions. The latter is not something that I as developer could judge as I likely don't have knowledge about the specific implementation of the platform the code is running on (if I know the platform in advance at all)
So it seems to me, STC shifts the problem of deciding to the developer even though the developer has no good tools to actually solve it.
So I'm with you. Just give me TCO every single time -- or give it to me never. Optimising it by hand is not that hard, and it's what most non-functional programmers do most of the time anyway. You'll see loops where recursion would be clearer, but then when you look at it hard you'll realise that it's just the TCO optimised version of the same code.
Otherwise, you may decide to just use recursion everywhere and then your stack overflows when your N is bigger than expected.
When in doubt, don't use recursion and don't expect that you get TCO.
If I write web apps, as far as I'm concerned, my platform is V8, Whatevermonkey, Trident and every other hypothetical javascript engine out there, any of which might or might not support TCO under different circumstances.
> When in doubt, don't use recursion and don't expect that you get TCO.
I think relying on TCO is always a bad idea, exactly because you can't be sure your platform supports it. But STC doesn't change that. You can't expect the platform to support tail calls because you did some magic incantations.
On the other hand, it doesn't make sense to me to forbid tail calls either. If you know your recursive code will work without TCO, it will work with TCO, too.
Small summary code: http://paste.ubuntu.com/24568118/
I know that's not the gdb-like experience you're looking for, but as far as I've found it's the best approach. The issue is that modern JS engines achieve their speed by tracking what happens at runtime and dynamically re-optimizing hot functions - so microbenchmarks are largely meaningless, and the performance of a given function can hugely affected by code that's far away.
(The above is for everyday. For extreme deep-diving, one can use JS engine tools to see what kind of internal representation your code has been compiled into after it got optimized. In chrome this is done with IRHydra - http://mrale.ph/irhydra/2/ .)
But an important piece of advice is at the bottom of one of their pages [1]: "Avoid micro-optimizing your JavaScript". Apart from their argument there, keep in mind you are programming for a number of very different runtime environments. An optimization that gives you a big boost in one implementation may slow you down on another one. That is true not just between various vendors but also among runtime versions from the same vendor.
[0] https://developers.google.com/web/tools/chrome-devtools/eval...
[1] https://developers.google.com/web/fundamentals/performance/r...
Example (memory profiling with heap snapshots): https://developers.google.com/web/tools/chrome-devtools/memo...
Profiling functions (V8, Chrome, using the CPU Profiler): https://developers.google.com/web/tools/chrome-devtools/rend...
What is missing there, I can see how long each function took, even more so when I combine it with the flame chart?
I assume you are still talking about the Javascript part. What the C++ based subsystem does can be examined using tools for that language on the respective platform.
Yes I know asking for an explanation causes a lot more nasty voting. This is ridiculous, the site is getting more and more like reddit, and I blame the site's maintainers: There are soooo many things that could be done for a more civilized discussion culture. Like making votes public, meta-voting, requiring explanations for downvotes, a very small downvote pool (e.g. no more than three downvotes per day), etc.
How about somebody would tell me what exactly is missing from those tools I linked to, or what is supposed to be wrong that I wrote? It's not like I insulted anyone. The guy I responded to had a lot less substance in his (short) comments. "It's very superficial" - what is superficial? What is missing? Also, "all I want are two numbers at the end of the process. How long did this take and how much memory did this take." -- well, he gets those number with the DevTools! So what exactly is the complaint? For someone with a question he doesn't seem to try very hard to get an answer. Then someone downvotes posts that try to get more out of him.
If you look at the documentation of what I am actually interested it is time-timeEnd or profile-profileEnd. With profile end I just don't get output on console and have to jump back and forth between these views looking at a ton of crap I am not interested in. With timeEnd I get the number I want but would like to see a "safe" approved way of where to place the timeend while dealing with async/promises/recursion separately and together.
I am used to testing different approaches within a function. Running the approaches overnight and just getting the 2 numbers printed out for each. Only if these deviate from expectations does the timeline and flamechart etc become useful. But Google debugging implementation seems to assume perf optimization is done in the opposite direction.
Measuring asynchronous code? You are not measuring your code. Your code is only the synchronous parts, the time spent in asynchronous parts are "environmental factors". Your code doesn't run during those times, it sits in the event loop waiting for an event so that it can continue.
https://github.com/mafintosh/nanobench
For debugging llnode is super good: