(Please excuse my English)
Below is a very simplified example code.
function test(limit) {
let count = 0, undone = true;
function inc() {
// do something
count++;
if(count == limit)
undone = false;
}
while(undone) {
// do something
inc();
}
}
I'm using nested function style functions very often, like inc() in the above example. Because it is very convenient to share variables.
However, someone told me that my coding style is very harmful. Because everytime test() (in the above example) is called, the inc() is allocated in memory repeatedly. He advised me to use ES6 class style code instead.
I could not agree with him, but not sure. Is nested function style code really harmful than ES6 class style?
Edit
I performed a simple benchmark for this question, as posted below.
https://stackoverflow.com/a/51530670/7339376
The result is somewhat astonishing to me. I was wrong. My adviser is right.
Edit At first, the title of this post was "Closure style code is...". I I modified it to "Nested function style code is...".
For the example above there is no significant difference in theory between using a closure to maintain a private variable and using a class for the same thing.
Using a closure will allocate a new function object each time the test function is called.
Using a class will allocate a new object each time the test function is called.
In theory, both allocate memory and both spend time calling a constructor.
There are those who may object to the fact that the function will need to be recompiled each time while a method in a class is compiled only once. Note that the closure can also be optimised to compile only once. So it's only a micro-optimisation issue.
Academically, closures and classes are equivalent. I think we first understood this when Lisp implemented objects and classes as a library using closures without any modification to the language but since then there have been research papers published proving that closures and classes are computationally equivalent - only they have slightly different feature sets.
When it comes to optimisations, the only thing you should do is benchmark your code. If it is good enough then don't change your code. Any opinion to the contrary is only religious/political and should be ignored. If you need better performance then profile your code to find the real bottleneck. This may be the bottleneck or it may not. Do not optimise code that is not slowing you down.
At the end of the day write the clearest, most readable code you can.
(Please excuse my English)
I just performed a benchmark using my Ubuntu 16.04 machine with perf command.The result was quite a surprise to me. I was wrong. My adviser is right.
My simple ES6 class style code is 2~3 times efficient than nested function style equivalent.
Below is class.js, ES class style, simple loop code.
class test {
constructor(limit) {
this.limit = limit;
this.count = 0;
this.undone = true;
while(this.undone) {
this.inc();
}
}
inc() {
this.count++;
if(this.count == this.limit)
this.undone = false;
}
}
for(let i=0; i<5000000; i++) {
new test(100)
}
And the result of perf stat node class (performed 3 times).
Performance counter stats for 'node class':
1055.290800 task-clock (msec) # 1.002 CPUs utilized
28 context-switches # 0.027 K/sec
6 cpu-migrations # 0.006 K/sec
2,554 page-faults # 0.002 M/sec
2,858,088,136 cycles # 2.708 GHz
892,221,452 stalled-cycles-frontend # 31.22% frontend cycles idle
<not supported> stalled-cycles-backend
5,815,629,017 instructions # 2.03 insns per cycle
# 0.15 stalled cycles per insn
2,561,547,866 branches # 2427.338 M/sec
6,776,303 branch-misses # 0.26% of all branches
1.053429789 seconds time elapsed
Performance counter stats for 'node class':
1057.919398 task-clock (msec) # 1.002 CPUs utilized
28 context-switches # 0.026 K/sec
6 cpu-migrations # 0.006 K/sec
2,555 page-faults # 0.002 M/sec
2,856,736,277 cycles # 2.700 GHz
890,790,850 stalled-cycles-frontend # 31.18% frontend cycles idle
<not supported> stalled-cycles-backend
5,815,147,889 instructions # 2.04 insns per cycle
# 0.15 stalled cycles per insn
2,561,451,165 branches # 2421.216 M/sec
6,778,756 branch-misses # 0.26% of all branches
1.056058390 seconds time elapsed
Performance counter stats for 'node class':
1054.245840 task-clock (msec) # 1.002 CPUs utilized
27 context-switches # 0.026 K/sec
9 cpu-migrations # 0.009 K/sec
2,553 page-faults # 0.002 M/sec
2,856,022,458 cycles # 2.709 GHz
890,300,670 stalled-cycles-frontend # 31.17% frontend cycles idle
<not supported> stalled-cycles-backend
5,815,241,984 instructions # 2.04 insns per cycle
# 0.15 stalled cycles per insn
2,561,469,424 branches # 2429.670 M/sec
6,768,183 branch-misses # 0.26% of all branches
1.052295061 seconds time elapsed
function test(limit) {
let count = 0, undone = true;
function inc() {
count++;
if(count == limit)
undone = false;
}
while(undone) {
inc();
}
}
for(let i=0; i<5000000; i++) {
test(100)
}
And the result of perf stat node nested (also performed 3 times).
Performance counter stats for 'node nested':
2377.214932 task-clock (msec) # 1.002 CPUs utilized
389 context-switches # 0.164 K/sec
9 cpu-migrations # 0.004 K/sec
3,141 page-faults # 0.001 M/sec
6,560,657,082 cycles # 2.760 GHz
1,946,461,285 stalled-cycles-frontend # 29.67% frontend cycles idle
<not supported> stalled-cycles-backend
16,046,574,530 instructions # 2.45 insns per cycle
# 0.12 stalled cycles per insn
6,110,865,652 branches # 2570.599 M/sec
6,953,209 branch-misses # 0.11% of all branches
2.371426270 seconds time elapsed
Performance counter stats for 'node nested':
2381.292759 task-clock (msec) # 1.002 CPUs utilized
391 context-switches # 0.164 K/sec
8 cpu-migrations # 0.003 K/sec
3,142 page-faults # 0.001 M/sec
6,558,376,504 cycles # 2.754 GHz
1,943,542,624 stalled-cycles-frontend # 29.63% frontend cycles idle
<not supported> stalled-cycles-backend
16,046,969,491 instructions # 2.45 insns per cycle
# 0.12 stalled cycles per insn
6,110,955,583 branches # 2566.234 M/sec
6,967,852 branch-misses # 0.11% of all branches
2.375578434 seconds time elapsed
Performance counter stats for 'node nested':
2376.579401 task-clock (msec) # 1.003 CPUs utilized
388 context-switches # 0.163 K/sec
7 cpu-migrations # 0.003 K/sec
3,125 page-faults # 0.001 M/sec
6,562,861,447 cycles # 2.761 GHz
1,947,165,390 stalled-cycles-frontend # 29.67% frontend cycles idle
<not supported> stalled-cycles-backend
16,051,805,939 instructions # 2.45 insns per cycle
# 0.12 stalled cycles per insn
6,111,984,155 branches # 2571.757 M/sec
6,962,744 branch-misses # 0.11% of all branches
2.369557403 seconds time elapsed
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With