100x100 grid draw at 2 FPS

Hi first time I'm using p5.js and not such experience with javascript I'm drawing a grid of 100x100 cells to make a cellular automata thing. I'm not a super guru about algorithms but if I want to redraw all the grid I cannot avoid a nested loop in more optimized way then:

  for(var i=0; i<this.rows; ++i) {
    for(var j=0; j<this.cols; ++j) {
        ...
    }
  }

Moreover I have to say that each loop I take a row and generate a new one creating a new array (i.e. allocating new variable and not re-using old one):

var oldRow = grid.getRow(rowcount);
var newRow = solver.generate(oldRow); // in here I create a new array: var newRow = [....]
rowcount++;
grid.setRow(rowcount, newRow);

It seems to me that 2 FPS is really slow.. Is it possible or I have to do something wrong somewhere else?

Answers

  • again here, sorry.. any comments?

    this is a minimum working example and it is super slow, am I missing something? any hints on any way to make it faster? or any other ways to achieve the same result?

    var w=100;
    var h=100;
    var cellW = 640/w;
    var cellH = 400/h;
    var cells = [];
    
    function setup() {
      createCanvas(640, 400);
    
      for(var x=0; x<w; x++) {
        var row = [];
        for(var y=0; y<h; y++) {
            row.push(1);
        }
        cells.push(row);
      }
    }
    
    function draw() {
      for(var i=0; i<w; i++) {
            for(var j=0; j<h; j++) {
                var value = cells[i][j];
                fill(value ? 255 : 0);
                stroke(0);
                var x = i*cellW;
                var y = j*cellH;
                rect(x,y,cellW, cellH);
            }
        }
    }
    
  • edited July 2015 Answer ✓

    This is my best attempt so far. It still looks too grim to me though: :|

    /**
     * CellGrid (v2.0)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11605/100x100-grid-draw-at-2-fps
     * studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ/latest
     */
    
    const W = 640, H = 480, FPS = 30,
          COLS = 100, ROWS = 80,
          WIDE = W/COLS, TALL = H/ROWS,
          ON = Object.freeze(color(255)), OFF = Object.freeze(color(0)),
          RESHUFFLE = true, // change it to false to stop auto-reshuffle.
          cells = new Uint8Array(COLS*ROWS);
    
    function setup() {
      createCanvas(W, H);
      noSmooth().noStroke().rectMode(CORNER).frameRate(FPS);
      RESHUFFLE || randomCellRefill();
    }
    
    function draw() {
      RESHUFFLE && randomCellRefill();
      console.log(frameRate());
    
      for (var col = 0, row = 0, cur = 0, tall = 0, now, last;
           row !== ROWS; cur = ++row*COLS, tall = TALL*row, col = 0)
        while (col !== COLS) {
          (now = cells[cur + col]) !== last && fill(last = now? ON : OFF);
          rect(WIDE*col++, tall, WIDE + 1, TALL);
        }
    }
    
    function mousePressed() {
      RESHUFFLE || randomCellRefill();
    }
    
    function randomCellRefill() {
      for (var idx = cells.length; idx--; cells[idx] = random(1) < .5);
    }
    
  • Hi gotoloop thanks you very much for the effort and for the hands-on micro javascript lesson : )

    I'm still not used to such a strict js, even if I appreciate a lot the language and how it can be "short & strict"

    To be honest the only changes I have truly understood are: - noSmooth() and frameRate() - Uint8Array (I suppose it is faster then a normal double array) - Object.freeze (it is not very clear to me how you compiled your piece of code because, even in the p5 official editor the colour function is undefined)

    All the other strict-ness in the loops I think that are micro-optimisation language specific. Are they?

    Anyway I have tried to report back changes in my sketch (that is quite elaborate - I posted only a minimum working example) and it has not changed so much, I still get 3-4 FPS. BUT I didn't changed all the loops (I have few classes and some cellularautomata-like algorithm that is not easy to change with micro-optimization.. it is a messy result of some personal rumination against CA and evolution system and it is really easy to achieve the non-sense without encapsulation in classes)

    Anyway, I have tried with mrdoob stats.js and your sketch is still 8-10 fps, that is way far from 30fps of normal java processing…

    thanks anyway!

    I am still curious how fast would be using the javascript 2d vanilla canvas..

  • edited July 2015

    I'm still not used to such a strict js,...

    That is anything but strict! It's more like outta-this-world exotic optimizations! :))

    Even in the p5 official editor the color() function is undefined...

    Don't use that only-for-Mac "official" editor! It's rather extremely old (v0.3.0)! [-X
    Right now, p5.js is at version 0.4.6 from 2015/Jun/24th: http://p5js.org/download/
    We can also test p5.js sketches by pasting them here: http://p5js.org/reference/#/p5/clear

    P.S.: Links for Int8Array typed-array container: :-B
    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array
    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

    And Object.freeze() too: O:-)
    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

  • I seriously doubt whether any micro optimisations are going to make any significant difference because the frame rate should not be that low. I can see absolutely nothing in your code that could be causing the low framerates. Looping through 10000 cells should be a doddle.

    I am no expert or fan of Javascript but I did manage to create a variation of Conway's Game of Life using pure Javascript in Eclipse and you can see that on my website it shows that JS is more than fast enough, in fact if I remember I had to cap the framerate.

    So I don't know what the problem is but I don't believe it is your code (based on what you have posted here)

    Hope you find the solution.

  • edited July 2015

    Just to clear things out, I've adapted the p5.js sketch to use pjs framework too.
    I guess it was just tiny faster. Dunno why it's still so slow... :-??

    http://studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ/rev.3

    /**
     * CellGrid (v2.0)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11605/100x100-grid-draw-at-2-fps
     * studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ/latest
     */
    
    const W = 640, H = 480, FPS = 30,
          COLS = 100, ROWS = 80,
          WIDE = W/COLS, TALL = H/ROWS,
          ON = -1, OFF = 0,
          RESHUFFLE = true, // change it to false to stop auto-reshuffle.
          cells = new Uint8Array(COLS*ROWS);
    
    void setup() {
      size(W, H, JAVA2D);
      noSmooth(); noStroke(); rectMode(CORNER); frameRate(FPS);
      RESHUFFLE || randomCellRefill();
    }
    
    void draw() {
      RESHUFFLE && randomCellRefill();
      println(frameRate);
    
      for (var col = 0, row = 0, cur = 0, tall = 0, now, last; row != ROWS; 
           cur = ++row*COLS, tall = TALL*row, col = 0)
        while (col != COLS) {
          (now = cells[cur + col]) != last && fill(last = now? ON : OFF);
          rect(WIDE*col++, tall, WIDE + 1, TALL);
        }
    }
    
    void mousePressed() {
      RESHUFFLE || randomCellRefill();
    }
    
    function randomCellRefill() {
      for (var idx = cells.length; idx--; cells[idx] = random(1) < .5);
    }
    
  • edited July 2015

    Made an even extra attempt. This time I've got rid of fill() within the loop.
    And it only issues rect() for the ON cells[]! *-:) Check out latest v3.0: B-)

    http://studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ/rev.7

    /**
     * CellGrid (v3.0)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11605/100x100-grid-draw-at-2-fps
     * studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ/latest
     */
    
    const W = 640, H = 480, FPS = 30,
          COLS = 100, ROWS = 80,
          WIDE = W/COLS, WIDE_PLUS = WIDE + 1, TALL = H/ROWS,
          ON = -1, OFF = 0,
          RESHUFFLE = true, // change it to false to stop auto-reshuffle.
          cells = new Uint8Array(COLS*ROWS);
    
    void setup() {
      size(W, H, JAVA2D);
      noSmooth(); noStroke(); rectMode(CORNER); frameRate(FPS);
      fill(ON);
      RESHUFFLE || randomCellRefill();
    }
    
    void draw() {
      RESHUFFLE && randomCellRefill();
      println(frameRate);
    
      background(OFF);
    
      for (var col = -1, row = 0, cur = 0, tall = 0; row != ROWS;
           cur = ++row*COLS, tall = TALL*row, col = -1)
        while (++col != COLS)  cells[cur+col] && rect(WIDE*col, tall, WIDE_PLUS, TALL);
    }
    
    void mousePressed() {
      RESHUFFLE || randomCellRefill();
    }
    
    function randomCellRefill() {
      for (var idx = cells.length; idx--; cells[idx] = random(1) < .5);
    }
    
  • edited July 2015

    And for my latest extreme optimization, it's trying out DataView:
    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView

    Besides storing just the ON/OFF byte state as Uint8Array was doing, it's adding up x & y coordinates as 2 bytes each. Thus each cell is being representing as 5 bytes now. :-bd

    Unfortunately it didn't seem to get any performance boost. Perhaps it got even slower, dunno? :-S
    But anyways, here it is the newest approach: ~:>

    http://studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ/rev.11

    /**
     * CellGrid (v4.1)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11605/100x100-grid-draw-at-2-fps
     * studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ/latest
     */
    
    const W = 640, H = 480, FPS = 30,
          COLS = 100, ROWS = 80, BYTES = 5,
          WIDE = W/COLS, WIDE_PLUS = WIDE + 1, TALL = H/ROWS,
          ON = -1, OFF = 0,
          RESHUFFLE = true, // change it to false to stop auto-reshuffle.
          cells = new DataView(new ArrayBuffer(COLS*ROWS*BYTES));
    
    void setup() {
      size(W, H, JAVA2D);
      noSmooth(); noStroke(); rectMode(CORNER); frameRate(FPS);
      fill(ON);
      setCellCoords();
    }
    
    void draw() {
      RESHUFFLE && randomCellRefill();
      println(frameRate);
    
      background(OFF);
      for (var idx = 0; idx < cells.byteLength; idx += BYTES)  cells.getInt8(idx) &&
        rect(cells.getInt16(idx+1, true), cells.getInt16(idx+3, true), WIDE_PLUS, TALL);
    }
    
    void mousePressed() {
      RESHUFFLE || randomCellRefill();
    }
    
    function randomCellRefill() {
      for (var idx = 0; idx < cells.byteLength; idx += BYTES)
        cells.setInt8(idx, random(1) < .5);
    }
    
    function setCellCoords() {
      RESHUFFLE || randomCellRefill();
      for (var x, y, idx = 0; idx < cells.byteLength; idx += BYTES) {
        x = idx/BYTES%COLS * WIDE; y = (idx/BYTES/COLS | 0) * TALL;
        cells.setInt16(idx+1, x, true); cells.setInt16(idx+3, y, true);
      }
    }
    
  • edited July 2015

    Not giving up yet! Getting back to use a typed array. But this time an Int16Array. *-:)
    It's the same idea as the DataView. But now even the ON/OFF state is a short (16-bit).
    Still I guess it's still losing to v.3.0. Even though the latter relies on a double loop and re-calculate both coords every single time: @-)

    http://studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ

    /**
     * CellGrid (v5.2)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11605/100x100-grid-draw-at-2-fps
     * studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ
     */
    
    const W = 640, H = 480, FPS = 30,
          COLS = 100, ROWS = 80, ELEMS = 3, DIM = COLS*ROWS*ELEMS,
          WIDE = W/COLS, TALL = H/ROWS,
          WIDE_ADJ = WIDE + .5, TALL_ADJ = TALL + .5,
          ON = -1, OFF = 0,
          RESHUFFLE = true, // change it to false to stop auto-reshuffle.
          cells = new Int16Array(DIM);
    
    void setup() {
      size(W, H, JAVA2D);
      noSmooth(); noStroke(); rectMode(CORNER); frameRate(FPS);
      fill(ON);
      setCellCoords();
    }
    
    void draw() {
      RESHUFFLE && randomCellRefill();
      println(frameRate);
    
      background(OFF);
      for (var idx = 0; idx < DIM; idx += ELEMS)  cells[idx] &&
        rect(cells[idx+1], cells[idx+2], WIDE_ADJ, TALL_ADJ);
    }
    
    void mousePressed() {
      RESHUFFLE || randomCellRefill();
    }
    
    function randomCellRefill() {
      for (var idx = 0; idx < DIM; idx += ELEMS)
        cells[idx] = random(1) < .5;
    }
    
    function setCellCoords() {
      RESHUFFLE || randomCellRefill();
      for (var x, y, idx = 0; idx < DIM; idx += ELEMS) {
        x = idx/ELEMS%COLS * WIDE; y = (idx/ELEMS/COLS | 0) * TALL;
        cells[idx+1] = round(x); cells[idx+2] = round(y);
      }
    }
    
  • edited October 2015

    Getting back to p5.js framework and trying out a Cell class in order to store isOk, x & y properties there.
    Remember to run it under v0.4.6 at least: ;)

    http://p5js.ProcessingTogether.com/sp/pad/export/ro.Cf6RWyw2ANSO4h

    /**
     * CellGrid (v6.2)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11605/100x100-grid-draw-at-2-fps
     * studio.ProcessingTogether.com/sp/pad/export/ro.9KbaSot4iSjhZ
     * p5js.ProcessingTogether.com/sp/pad/export/ro.Cf6RWyw2ANSO4h
     */
    
    const W = 640, H = 480, FPS = 30,
          COLS = 100, ROWS = 80, DIM = COLS*ROWS,
          WIDE = W/COLS, TALL = H/ROWS,
          WIDE_ADJ = WIDE + .5, TALL_ADJ = TALL + .5,
          RESHUFFLE = true, // change it to false to stop auto-reshuffle.
          cells = Array(DIM);
    
    var on, off;
    
    function setup() {
      createCanvas(W, H);
      noSmooth().noStroke().rectMode(CORNER).frameRate(FPS);
    
      on = Object.freeze(color(255)), off = Object.freeze(color(0))
    
      fill(on);
      createCells();
    }
    
    function draw() {
      RESHUFFLE && randomCells();
      println(frameRate());
    
      background(off);
      for (var idx = 0; idx != DIM; cells[idx++].display());
    }
    
    function mousePressed() {
      RESHUFFLE || randomCells();
    }
    
    function randomCells() {
      for (var idx = 0; idx != DIM; cells[idx++].isOn = random(1) < .5);
    }
    
    function createCells() {
      for (var x, y, idx = 0; idx != DIM; ++idx) {
        x = idx%COLS * WIDE, y = (idx/COLS | 0) * TALL;
        cells[idx] = new Cell(x, y);
      }
    
      Object.freeze(cells);
      RESHUFFLE || randomCells();
    }
    
    function Cell(x, y) {
      this.isOn = false, this.x = round(x), this.y = round(y);
      Object.seal(this);
    }
    
    Cell.prototype.display = function () {
      this.isOn && rect(this.x, this.y, WIDE_ADJ, TALL_ADJ);
    }
    
    Object.freeze(Object.freeze(Cell).prototype);
    
Sign In or Register to comment.