Game of Life
- Watch: http://apps.gamejs.org/gameoflife/2opt/
- Code: https://github.com/oberhamsi/gameoflife-gamejs/
GameJs implementation of Conway's Game of Life http://en.wikipedia.org/wiki/Conway's_Game_of_Life.
The three commits represent the optimization stages this app went through:
Unoptimized simulation & drawing
A two-dimensional array containing booleans holds the cells alive status.
Two nested for loops update the simulation data by creating a new array, checking
each cell's neighbour count every frame. For every cell I draw a
At this point, the simulation part was the bottleneck: 30-40% of the CPU time
was spent in
countAliveNeighbors(): each cell re-calculates its
neighbours every frame.
Usuall, after a short while, the Game of Life simulation consists of mostly stable cells; i.e. cells that don't change their alive status.
Because so few cells change their status at a particular frame it is
cheaper to update the cached
neighborCount value of effected cells than to
re-calculate that value every frame.
And indeed, after caching the neighbour count for each cell, the new bottleneck
gamejs.draw.rect function taking up 25% of our time. This is the last
function we still call for each cell every frame.
gamejs.draw.rect is too slow to be called so often: it does a
canvas context switch, parameter checking & type conversions. If we could
directly set the couple of pixels this cell represents, instead of going
rect layer, we would probably be better off. There are just too
many, mostly small updates to the screen every frame.
gamejs.surfacearray.SurfaceArray is for such cases: the surface's individual
pixels are manipulated with
set() and the SurfaceArray later blit on
another Surface with a single
Additionally, I added the dirty flag 'isModified' to each cell. This is set if a cell's alive status changes - it isModified isn't set I leave the pixel status to what it is from last frame.