How busy is the CPU?

Posted on
  • Hi,

    While playing with Espruino and having a fun time learning JavaScript in general, I wonder:
    How busy is the CPU?
    The point came up when I created a "glow LED" function with gets called via setInterval() every 5ms.
    It works fine for 4 LEDs, but I wonder when it will start to drop things or simply run late. Can I do 10 LEDs? What about 20? 200? 2000?

    This is not an actual problem (yet), but it would need only some slow JavaScripts calls to keep the CPU busy, and I'd rather know that I am either close to the limit or far, far away from it.

    Harald

  • Hi Harald,

    You might find that setBusyIndicator(LED1) is useful for you? That will turn LED1 on whenever Espruino thinks it's busy. You can use any pin (not just LED1), so you could hook it up to an oscilloscope to see how much time it spends turned on.

    If you're just using LED1, waving the board side to side is still handy - it should leave a trail in your vision and you'll be able to guess roughly what percentage of the time it's on.

  • POV system monitor that's great.

  • Gordon,

    That's close to perfect and certainly does the job for me.

    Thanks for the quick reply!

  • @HaraldK, 'glow LEDn' for n = 20..2000? That's when the data daisy chained LEDs come in handy witht serially sending 32 bit per LED w/ 400kHz (1 data line) for timed detection or xMHz for clocked detection (1 data and 1 clock line): 24 for color and 8 for intensity. Are you thinking about driving a LED cube? ...or a 'crazy' Christmas decoration? With 400kHz you get about a frame rate of 50 frames/s on 2k LEDs, which gives you already smooth motion picture (movie)... you may be able to manage the 80kByes for the data buffer, but I'm not sure about preparing that data and and send it out at the same time... in (non-compiled) JavaScript. Compilation helps a lot, but is at this point still somewhat limited. You might need to share a bit more about your setup to get reasonable figures in the answers.

  • No, the example of 4-20000 LEDs was just that: an example. I know I can change the brightness of the 4 LEDs I have via setting the duty cycle via setInterval(). No issues. CPU power is enough even for updating once in 5ms per LED.
    I am quite confident thatI cannot do the same if I had 20000 LEDs. The question was: how do I know when the CPU won't be able to catch up anymore?
    And the setBusyIndicator() does that.

    BTW, I checked and this is the result:

    // Glow 4 LEDs
    
    var glow=function(led) {
      var intensity=0;
      var dir=1.0;
      var this_led=led;
      function intensity_step(n) {
        intensity+=dir*n;
        if (intensity>=1.0) {
          intensity=1.0;
          dir=-dir;
        }
        if (intensity<=0.0) {
          intensity=0;
          dir=-dir;
        }
        analogWrite(this_led, intensity);
      }
      return intensity_step;
    };
    
    var led1=glow(D13);
    setInterval(function() {
      led1(0.01);
      }, 2);
      
    var led2=glow(D14);
    setInterval(function() {
      led2(0.01);
      }, 2);
    
    
    var led3=glow(D12);
    setInterval(function() {
      led3(0.01);
      }, 2);
    
    var led4=glow(D15);
    setInterval(function() {
      led4(0.01);
      }, 2);
    
    // See how busy the CPU is
    
    setBusyIndicator(D11);
    
    

    Attached the wave form of D11 when changing only one LED. You can see this is repeating at 500Hz, and it's already using 25% of CPU.
    4 of those keep the CPU close to 100%.

    Please no one comment on the horrible code. I'm just learning JavaScript and I already figured out the code is sub-optimal in so many ways ^_^

    PS: Forgot to mention: This is on the STM32F4 Discovery board.


    1 Attachment

    • LED2.jpg
  • Ahh - I thought you were doing software PWM, rather than just updating at 500Hz (the Espruino and Espruino Pico can't do hardware PWM on their LEDs).

    But yes, that's about what I'd expect. By turning on 'offline minify' in settings you could probably get quite a big jump in speed with that.

  • Cool, great and quick response. Comment on the code: interesting... very clear to understand... I see that you already use PWM / analog output... I do not know about STM32F4 discovery board chip, whether it has HW or SW PWMs. HW PWMs should be a bit more performing; @Gordon can give the facts (...: provided them while writing this post). Another option could be the Waveform with single or double buffer... never used it, so I'm not sure if the buffer setting overhead eats up the gain. What about compiling the function? Have no experience with that... setup may have to be dfferent...

    From the code I read it is a pulsing - intensity from 0 to 1 and back in 0.01 steps - a step every 2ms... thus pulsing 2-1/2 times per second.

    Interesting in the code is its quasi object-orientation: a logical new by returning a seperate intensity_step function every time you invoke glow() function (taking advantage of the closure / lambda / functional programming capability of Javascript).

    I have some alternaties... if you'd like to know... ;\ - regarding performance difference? --- Would be interesting to know.

    To stick with your approach, passing in the increment/decrement stp with the led, an extra layer of function call is removed in setInterval();

    var pulse = function(led,stp) {
      var ity = 0;
      return function(){
        analogWrite(led, 
          (ity += stp) < 0
           ? ity = 0 + (stp = - stp)
           : (ity > 1)
             ? ity = 1 + (stp = - stp)
             : ity
         );
      };
    };
    setInterval(pulse(D13,0.01),2);
    setInterval(pulse(D14,0.01),2);
    setInterval(pulse(D12,0.01),2);
    setInterval(pulse(D15,0.01),2);
    

    What timing differences - if any - can you notice with this code?

    PS: Code has an issue not going all the way to 0 or 1 when 1 / step not a whole number.

  • Just ran it on a Pico (B3..B6). Added a setBusyIndicator(A8) and placed a diode from D8 to GND - shining pretty brigth, showing busy time - and then from 3.3 to D8 - shining much more dim, showing idle/sleep time, but still noticable.

  • Notice the 'funny' & and | in the set

    var pulse = function(led,stp) {
      var ity = 0;
      return function(){
        analogWrite(led, 
          (ity += stp) < 0
           ? ity = 0 & (stp = - stp)
           : (ity > 1)
             ? ity = (stp = - stp) | 1
             : ity
         );
        console.log(ity);
      };
    };
    

    Has the issue with repeating 0 and 1 with 1 / stp being a whole number, but working for all other stp. Changing the comparison from < and > to <= and >= works just the other way round.

  • Here the picture of the pocket oscilloscope. Again, only one LED active.
    A small performance increase I can see. Your code uses 340us with or without the "| 1".
    Clever use of ?: here. You must have used C before ^_^

    My original code uses 440us.


    1 Attachment

    • DSC_0722.JPG
  • Different, slightly unrelated question:
    I tried to use

    var led1=flow(D13);
    setInterval(led1(0.01),2);
    

    but I get an error:

    >setInterval(led1(0.01),2);
    =undefined
    Uncaught Error: Function or String not supplied!
     at line 1 col 25
    setInterval(led1(0.01),2);
    

    Anyone an idea why that does not work? led1 is a function after all when I use it as led1()...

  • ...sure, setIntval() is an immediately executed function with parameters. When passing the - executable - led1 function with parms as first parm - and not just it's reference = it is invokeked and the retrun value is passed as actual parameter - in this case undefined by the definition of the 'intensity_step' functin. The returned value is stored in the time event list / scheduled as the function to call when the interval lapses...

    setInterval(arg0, arg1) is defined as follows: arg0 is either a string (with source code) or a function that can be evaluated respective called on interval lapse.

    Therefore, you could have provided setInterval("led1(0.01)",2); (with led1 known in root context) and that works as well... I don't know about the performance, but generally, I prefere the reference of a function or an anonymous function over the passing of a string, which can be very tricky when having to pass the values of parms that are not as easy as numbers - and even worse -- variable that can change their value (depending on Javascript unique look at scope)... (one of the 'not so nice' side effect of JavaScript....)

  • Ok, I officially am a JS noob...thanks for the explanation. At first I did not see the difference, but I saw what to check.
    I kind'a get it now and this helped:

    var glow=function(led, stp) {
      var intensity=0;
      var dir=1.0;
      var this_led=led;
      var this_stp=stp;
      function intensity_step() {
        intensity+=dir*this_stp;
        if (intensity>=1.0) {
          intensity=1.0;
          dir=-dir;
        }
        if (intensity<=0.0) {
          intensity=0;
          dir=-dir;
        }
        analogWrite(this_led, intensity);
      }
      return intensity_step;
    };
    
    var led1=glow(D13,0.01);
    setInterval(led1, 2);
    

    Is that behavior a generic JS thing or the implementation of setInterval()?

  • it is how setInteval (and setTimeout) is (are) defined... The nice thing in javascript is that an anonumous function can be created that wraps the actual function invocation.

    If you would have to implement it in an other language, you would also pass the reference to be invoked funciton for the time when the time comes and not right away.

    Unique javascript (and some other languages since the 70) is the functional / lambda / closure programming: code and context as a package are provided as an entity for execution (repeatedly) at a later time(see array example below). Therefore you do not need extra variables in line 4 and 5. At invocation those are generated and you can keep working with them in your intensity_step function...

    var ledSpecs = // array of led specifications with led and on/off status
    [ {led: D12, state: false}
    , {led: D13, state: false}
    , {led: D14, state: false}
    , {led: D15, state: false}
    ];
    
    var toggle = function(ledSpec) {
         digitalWrite(ledSpec.led, (ledSpec.state = !ledSpec.state)); // toggle
    };
    
    setInterval(function(){ // toggle every 333 ms all leds
        ledSpecs.forEach(toggle); 
    },333};
    

    ... or in short form with anonymous toggle function:

    var ledSpecs = // array of led specifications with led and on/off status
    [ {led: D12, state: false}
    , {led: D13, state: false}
    , {led: D14, state: false}
    , {led: D15, state: false}
    ];
    
    setInterval(function(){ // toggle every 333 ms all leds
        ledSpecs.forEach(function(ledSpec){// iterate over every element in the array
            digitalWrite(ledSpec.led, (ledSpec.state = !ledSpec.state)); // toggle
        });
    },333); 
    

    ...now, you can make a pulsing wave through your four leds (...n leds in a circle), by replacing state with intensity... by pulling intensity from a intensity constantly circulary shifting versus the led array```

    specArray.forEach(function(element,index­,array){
        /* analog out for current led as specified in element */
        /* your code using element,index,array... to set new intensity  */
    });
    
  • Oh, now I got it! Of course you would pass the reference! Having one parameter made this pass-the-reference difficult or even impossible without using an anonymous function or the string of what to execute.

    Thanks a lot. I am now 1 step closer to understanding JavaScript ^_^

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

How busy is the CPU?

Posted by Avatar for HaraldK @HaraldK

Actions