Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

node.js : Nested function style code is harmful than ES6 class style?

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

like image 394
pdh0710 Avatar asked May 20 '26 08:05

pdh0710


2 Answers

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.

like image 132
slebetman Avatar answered May 22 '26 21:05

slebetman


(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


Below is `nested.js` (nested function style equivalent to class.js)
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
like image 37
pdh0710 Avatar answered May 22 '26 22:05

pdh0710



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!