Introduction
Drawing (a lot) of circles (or anything for that matter) on a particular HTML5 canvas can really bog down performance. This little bit of code is an attempt to illustrate the performance difference between using a constant vs. always using 2*Math.PI
when using the .arc()
method to draw a circle to a canvas.
Background
I have a project that involves drawing an insane amount of circles to an HTML5 canvas element. During development, I ran into a performance issue. The number of circles drew went up almost exponentially at one point and so the performance went down (exponentially) at the same time.
The performance issue itself has since been solved by other means, but when the issue arose, it prompted me to write this little bit of JavaScript code.
I figured I could save some time by calculating 2 * Math.PI
and placing the result into a variable to be called by a draw circle function, vs. having the code calculate the number every time drawCircle()
as called. So... I wrote a quick script, the results were counter intuitive to me... so I thought I'd share them.
Using the Code
I'm going to illustrate the entirety of the script (it's fairly small) so you can run this on your own. You can take the sections of code I place here and either dump it in an HTML file between <script></script>
tags or a separate .js file... as you wish.
The script itself creates the Canvas
element, so there really aren't any HTML requirements besides having an empty HTML page.
First, let's go ahead and set up our canvas and then create our context:
This will give us something to draw on...
var canvas = document.createElement("canvas");
canvas.width = 200;
canvas.height = 200;
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
Now, let's create some basic variables to control the test and hold the results:
var startTime;
var timeList = new Array();
var timeList2 = new Array();
var circleDraws = 1000;
var testLoops = 10;
var preCalcTP = 6.28318530718
Create a function to draw a circle:
var drawCircle = function(tpr) {
ctx.beginPath();
ctx.arc(50,50,50,0,tpr);
ctx.stroke();
};
Put some loops in place to draw the circles:
for (x = 0; x < testLoops; x++){
ctx.clearRect(0,0,canvas.width,canvas.height);
startTime = Date.now();
for (i = 0; i <= circleDraws; i++)
{
drawCircle(preCalcTP);
}
timeList.push(Date.now() - startTime)
}
for (x = 0; x < testLoops; x++){
ctx.clearRect(0,0,canvas.width,canvas.height);
startTime = Date.now();
for (i = 0; i <= circleDraws; i++)
{
drawCircle(2*Math.PI);
}
timeList2.push(Date.now() - startTime)
}
And finally... let's display the results:
var resultTable = "<table><thead><tr><th>Pre-" +
"Calculated</th><th>Calculated</th><" +
"th>Difference</th></tr></thead><tbody>"
var packTD = function(value) {
if (value != null) {
return "<td>" + value + "</td>";
} else {
return "<td> </td>";
}
};
for (i = 0; i < timeList.length; i++)
{
resultTable += "<tr>";
resultTable += packTD(timeList[i]);
resultTable += packTD(timeList2[i]);
resultTable += packTD(timeList[i] - timeList2[i]);
resultTable += "</tr>";
}
resultTable += "</tbody></table>";
var tableResults = document.createElement("tableResults");
tableResults.innerHTML = resultTable;
document.body.appendChild(tableResults);
Points of Interest
You'll see that after running this little script a few times, a pattern starts to emerge.
- Using
Math.PI * 2
is faster than using a var
with the number already calculated (which is odd to me). - The performance for each run goes up and down by about 30-50% each time you run it (for either set of test).
Take a look at the result table below to see what I mean.
Pre-Calculated | Calculated | Difference |
133 | 85 | 48 |
82 | 161 | -79 |
161 | 84 | 77 |
85 | 160 | -75 |
160 | 87 | 73 |
85 | 158 | -73 |
84 | 87 | -3 |
158 | 162 | -4 |
84 | 87 | -3 |
161 | 156 | 5 |
I just thought the results were interesting and that I would share... enjoy!