When does millis start?

edited April 2017 in Programming Questions

Real quick question, does millis start when you reach void draw or does it start at setup?

Answers

  • edited April 2017

    @Sjors -- millis() starts when the sketch starts -- in a sketch with or without setup. Calling millis(); immediately in setup will still occur a short delay after the sketch has spun up.

    Try these test sketches to see:

    1. no setup

      println(millis());
      

      911

    2. setup

      void setup(){
        println(millis());
        noLoop();
      }
      void draw(){
        println(millis());
      }
      

      917
      931

    3. setup with delays

      void setup(){
        noLoop();
        println(millis());
        delay(500);
        println(millis());
      }
      void draw(){
        println(millis());
        delay(500);
        println(millis());
      }
      

      1022
      1523
      1526
      2031

  • edited April 2017

    This is the implementation of PApplet::millis() method:
    https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PApplet.java#L3158-L3160

    public int millis() {
      return (int) (System.currentTimeMillis() - millisOffset);
    }
    

    As you can notice, it invokes System::currentTimeMillis(), subtracts its value from field millisOffset, and downcasts the total result to (int), returning it:
    http://docs.Oracle.com/javase/8/docs/api/java/lang/System.html#currentTimeMillis--

    Field millisOffset is declared and initialized this way:
    https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PApplet.java#L700

    long millisOffset = System.currentTimeMillis();
    

    Given it is initialized at the moment of its declaration, that fixed value is established at the very early moments of PApplet's instantiation.

    Which happens much earlier than setup() & even settings() are called back btW. >-)

    And if class PApplet had any explicit constructors, that'd happen even before them! =P~

  • edited April 2017

    A version of millis() that is keyed to the start of the first draw frame is as simple as recording an offset -- but finding that offset is a bit tricky.

    setup() runs during frame 0. That means that draw won't begin until the start of frame 1 -- for example, at frameRate(1) that would be 1000 millis after setup() is called, which is already some amount of delayed time after the sketch begins. So, if you check the time at the end of setup(), this may be much earlier than the start of draw() if there is still time left before frame 1 fires. Conversely, draw() may also begin much later the expected frame 1 time if setup() itself takes more than the duration of a frame to complete running.

    One solution is to record the startup offset at the beginning of setup, including the anticipated delay time of a single frame. Then, at the end of setup, check if the setup time took less then one frame. If it did, use the estimate. If it didn't, use the current time. This will get your timer within a few milliseconds of 0 when draw is called for the first time.

    // A clock based on draw()
    // forum.processing.org/two/discussion/22272/when-does-millis-start
    int FPS = 10;
    int offset;
    
    void setup() {
      // first line -- offset is current time plus estimated length of frame 0
      offset = millis()+(int)(1000.0/FPS);
      frameRate(FPS);
      // the rest of setup goes here
      // simulate setup process
      delay((int)random(0,2000));
      // last line -- offset is the longer of either:
      // 1. our estimate (wait until frame 1)
      // 2. the current time (if setup took longer than a frame to run)
      offset=max(offset, millis());
    }
    
    void draw() {
      println(dmillis());
    }
    
    // clock with 0 close to the first time draw() ran
    int dmillis() {
      return millis()-offset;
    }
    
  • /**
     * Draw Millis (v1.0)
     * GoToLoop (2017-Apr-28)
     * forum.Processing.org/two/discussion/22272/when-does-millis-start#Item_4
     */
    
    long drawMillStart;
    
    @ Override int millis() {
      return (int) (System.currentTimeMillis() - drawMillStart);
    }
    
    void setup() {
      size(300, 150);
      frameRate(1);
      registerMethod("pre", this);
    }
    
    void pre() {
      unregisterMethod("pre", this);
      drawMillStart = System.currentTimeMillis();
    }
    
    void draw() {
      final int currMillis = millis();
      getSurface().setTitle("FC: " + frameCount + "  -  Millis: " + currMillis);
      background((color) random(#000000));
    }
    
  • edited April 2017

    @GoToLoop -- nice use of a one-time pre() that unregisters itself -- and with it, nice paired use of both Override, and of System.currentTimeMillis() to get around how to initialize the override.

    A registered method pre() is executed just before draw(), so this is another way around the issue of timing startup and setup().

    Both of the solutions above are ways to avoid something like this third, less elegant solution, which is more concise and gets the idea across without fancy tricks -- but which has the drawback of requiring checking the if every frame for the rest of the sketch even though it only runs a single time on the first draw frame:

    int drawMillisStart;
    void draw(){
      if(frameCount==1){
        drawMillisStart = millis();
      }
      int currMillis = millis() - drawMillisStart;
      println(currMillis);
    }
    

    Note that in all three cases resetting the sketch by setting frameCount=0 will reset the clock:

Sign In or Register to comment.