The “make it fast” demo
Yesterday I wrote about making code work then nice then fast. I believe that the fast part was not that obvious on how it can impact your overall game performance. So I decided to put up a simple test that is a demonstration of a real world example.
We will be dealing with two of the finest cpu-cycle hogs in the Actionscript world and you can pretty much apply it to every thing game development related.
The main code
I defined this as the document class of some flash file and then started to add methods to test. Each method was a variation of the previous. At each test I simply added the function call to the constructor. Simple and fast, here’s the first method…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package { import flash.display.MovieClip; import flash.geom.Point; import flash.utils.getTimer; public class PerfTest extends MovieClip { public function PerfTest() { // The start time var startTime:int = getTimer(); // Test method call goes here // Traces the test results trace("Test concluded in " + String(getTimer() - startTime) + " miliseconds"); } } } |
Calculating a distance: 1750ms
The basic test is to calculate the distance between two points. I simply wrote every single detail of code I needed inside a 1 million iteration ‘for’ loop. I created new points, calculated the right triangle legs and then the hypotenuse. Every thing should be crystal clear. One million distances calculated got an average time of around 1750 miliseconds.
1 2 3 4 5 6 7 8 9 10 11 12 13 | private function DistanceTest1() { for (var test:int = 1; test <= 1000000; test++) { var point1:Point = new Point(100, 100); var point2:Point = new Point(200, 200); var leg1Length:Number = Math.pow(point1.x - point2.x, 2); var leg2Length:Number = Math.pow(point1.y - point2.y, 2); var distance:Number = Math.sqrt(leg1Length + leg2Length); } } |
Skipping object creation: 505ms
This is one of the biggest issues, one that is often a problem. I’ve read about it, I’ve even defended that object pooling is not that important in many games and then once my code created around 200 objects in less than a second I had to rethink it. Add to that that the objects created had things to do on their own, like moving, colliding and sorts and I got myself a bit of a performance issue.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private function DistanceTest2() { var point1:Point = new Point(); // Created outside the loop var point2:Point = new Point(); // Created outside the loop for (var test:int = 1; test <= 1000000; test++) { point1.x = point1.y = 100; // Reused object point2.x = point2.y = 200; // Reused object var leg1Length:Number = Math.pow(point1.x - point2.x, 2); var leg2Length:Number = Math.pow(point1.y - point2.y, 2); var distance:Number = Math.sqrt(leg1Length + leg2Length); } } |
The only thing done is here to take the creation of new objects from the loop. Note that I’m not reusing Numbers because the impact is on the creation of an object with the ‘new’ operator.
Other ways to do math: 180ms
The result is the same but the time taken to do it isn’t. Math.pow(x,y) and other methods from the Math static class are very heavy. There are times where it is too troublesome not to use it, but if you can avoid it, do so.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private function DistanceTest3() { var point1:Point = new Point(); var point2:Point = new Point(); for (var test:int = 1; test <= 1000000; test++) { point1.x = point1.y = 100; point2.x = point2.y = 200; // Math.pow(x,y) substituted with a simple multiplication var leg1Length:Number = (point1.x - point2.x) * (point1.x - point2.x); var leg2Length:Number = (point1.y - point2.y) * (point1.y - point2.y); var distance:Number = Math.sqrt(leg1Length + leg2Length); } } |
One last math optimiztion: 20ms
Calculating square roots is heavy stuff, really heavy stuff. This is not the exact same case as the one pointed previously, but it is very similar. The main difference here is that if you are returning the distance, that is not the correct distance at all. It is the distance powered by two. If you want to use this distance to check against something, you’ll need to power that something also to keep it fast.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private function DistanceTest4() { var point1:Point = new Point(); var point2:Point = new Point(); for (var test:int = 1; test <= 1000000; test++) { point1.x = point1.y = 100; point2.x = point2.y = 200; var leg1Length:Number = (point1.x - point2.x) * (point1.x - point2.x); var leg2Length:Number = (point1.y - point2.y) * (point1.y - point2.y); // No square root is calculated, you need to take this into consideration var distance:Number = leg1Length + leg2Length; } } |
Conclusions
I tried to address some common performance downfalls that happen very easily but that can be dealt with very easily also. We can argue that this is over one million iterations, but if you are using this kind of code for collision detection, that amount of iterations can happen sooner than you’d expect.
The real problem with performance in game development is not at all a matter of having millions of operations in one spot of your game loop like it is demonstrated here, but having several minor performance issues all around your code that is called from the game loop.
This is just a demo of the kind of things you should look for and solve. There are many others, some of it very game specific. When your game is working as intended, when the design is implemented, it’s time to make it fast because the game should be as smooth as possible and because you’ll probably and hopefully polish the game. That polishing often adds something to the game and that something needs breathing room to work out well.
Hope this helps,
Vlad
Posted: August 5th, 2009
at 12:00am by Vlad
Tagged with FlashGameBlogs, Testing
Categories: The code of VGS
Comments: 5 comments
