How to pinpoint slow areas of a program

BlitzMax Forums/BlitzMax Beginners Area/How to pinpoint slow areas of a program

Ranoka(Posted 2011) [#1]
Does anyone have any tips for locating the bottlenecks in the code of a game.

My game is really simple! (It's a re-make of a spectrum game) but it's running much slower than expected, so there must be something wrong but I'm not sure how to locate the problem.

Something like timing how long the program spends running each part of the code so you know which areas need improving or might have some bugs that are slowing the game down.

I'm doing this in my code (not every frame) and it looks like the memory is being released.
Print GCMemAlloced()


I'd rather people give me some pointers so I can figure it out on my code myself rather than giving my code and asking someone to fix it -- I'll learn and understand more that way.

Thanks!


GfK(Posted 2011) [#2]
I do this:
Local s:Int = Millisecs()
'some function calls here
DebugLog Millisecs() - s



Ranoka(Posted 2011) [#3]
Thanks, I will try this out.
I have a suspicion it'll be something to do either with my Lists, my drawing routine or some dodgy loop or something.


Czar Flavius(Posted 2011) [#4]
Here are some things to look out for:

Never use load image or anything which accesses the harddrive in your main game loop.

Make sure each thing that needs to be updated once is only updated once.

Don't use TList.ValueAtIndex or TList.Remove. Use TLinks to remove from lists efficiently. I can give an example if required.

Avoid array resizing or slicing.

Don't redraw the whole game scene, only what's visible on the screen. (Unless everything is visible anyway)

Don't use floats where ints will do.

If a Type is being created and deleted extremely often, it's faster to "reset and reuse" an old, unneed instance of the object than to create a new one. But don't become paranoid about that. But if you're making a particle effect for instance, you should consider it.

Avoid converting between strings and numbers often.

All I can think of for now!

Last edited 2011


Doc Holliday(Posted 2011) [#5]
@Czar Flavius: you wrote "Use TLinks to remove from lists efficiently. I can give an example if required."

I'm a bmax beginner too. It would be nice to see some example.

Thanks

Doc Holiday


Czar Flavius(Posted 2011) [#6]
A list stores objects in a kind of chain. Each object has a link to the object behind it and the object in front of it. When you add a new object to the list, it creates a new link on the end and hooks up your new object. This is very fast to do.

Compare this to an array, where the computer reserve a whole block of memory to a certain size. You can't add new objects beyond the size of the array without causing an error. If you need the array to grow, you have to copy the block to a new bigger area. This is slow.

But the advantage of an array is that you can access a particular object immediately, if you know its index number. But as a list doesn't group all its objects in the same block of memory, you can't do the same thing. To find the 5th object for example, you must start at the beginning of the list and jump to the 2nd, 3rd, 4th and finally 5th object. If the list is very large, hundreds or thousands of objects, this can become very slow.

Usually when you go through a list, you want to do something to every object anyway (update/draw) and use a For EachIn loop anyway. The For EachIn loop gets one object, and then follows its link to the next for the whole list, so it's a very efficient way of doing something to every element on the list.

The usual problem with a list is when you want to remove something. An enemy has died and you want to remove him from your game objects list so he is no longer updated or drawn. TList.Remove will unfortunately have to scan the list, starting at the beginning, and hop over each object until it finds your object in question and then remove it. If the list is very big and you remove things often, this is very slow.

When you add something to a list, it will return to you a TLink object! This is the key - it gives you a direct shortcut to your object. Using the Value method, you can access the object straight away. Using the Remove method, you can remove the object from the list right away.

Here is a typical way I code my game objects.

Type TGameObject
	Global all_objects:TList = New TList
	Field link:TLink
	
	Field health:Int = 100
	
	Method New()
		link = all_objects.AddLast(Self)
	End Method
	
	Method update()
		If health = 0 Then kill()
	End Method
	
	Method draw()
		
	End Method
	
	Function update_all()
		For Local go:TGameObject = EachIn all_objects
			go.update()
		Next
	End Function
	
	Function draw_all()
		For Local go:TGameObject = EachIn all_objects
			go.draw()
		Next
	End Function
	
	Method kill()
		link.Remove()
		link = Null
	End Method
	
	Function kill_all()
		'shortcut
		all_objects.Clear()
	End Function
End Type


Last edited 2011


shinkiro1(Posted 2011) [#7]
Chances are high that the real bottlenecks of your program hide in loops. Because they are executed that often, it will add up and slow down your game.

If you have a large number of objects interacting with each other, you should always split them into logical groups. In a collision system, there is no need to check objects wich are far away from each other.
So you split your playfield into parts and only check each object in that specific part against any other objects in this part.

What I absolutely advice against although is optimization, where it's not needed. Ex: Optimizing the ini file reader so it only takes 1 ms instead of 2 ms to load your configuration file. The user doesn't care if he has to wait 1ms longer for the game, so always think if optimization is really needed.


Doc Holliday(Posted 2011) [#8]
@CZar Flavius:

Thank you for the example code. I now understand how you manage the task of removing objects.

But one further question:

In your example code you declare a field named link. But in your
new()-method you use _link. Why the underscore first?

Thanks

Doc Holiday

Last edited 2011


Czar Flavius(Posted 2011) [#9]
Sorry that was a typo! I will correct it.

In my code, I prefix certain fields with a "_" in order to show that they are special and not to be used by the code unless you have to. That way, chances of bugs are reduced as code only uses what it has to.


Ranoka(Posted 2011) [#10]
Wow, I pin-pointed my bottle neck.

My game logic is only 3 or 4 milliseconds per loop

But GrabImage alone goes up to using 200 odd milliseconds!
It starts at 100 ms, goes to 150ms and settles on 200ms...

Basically, what I'm trying to do is draw to a canvas, and then scale it up and draw that to the screen. The reason why is so the size can be scaled up and down without changing the draw logic (the actual game size is tiny).

I'm surprised it's going so slow, maybe there's a better approach to do what I'm trying.

Thanks GfK, the approach you gave me helped pinpoint by branching down into the area that had a big number.


shinkiro1(Posted 2011) [#11]
Do you want to Scale up everything, then you can just use
SetScale x,y

before drawing.


Brucey(Posted 2011) [#12]
Do you want to Scale up everything, then you can just use ... SetScale

Well, no, not really.
That only scales the size of things, it doesn't change the drawing locations based on the origin location.


Czar Flavius(Posted 2011) [#13]
Unless I'm mistaken, grab image needs to load a new copy of your image in system memory and then resend it back to the graphics card, which is a slow operation. It doesn't use the harddrive but grabimage can be considered a "loading" function that's not for the main game loop.

Try changing the scale and multiplying the distances between points by the scale too.


Brucey(Posted 2011) [#14]
Or see if you can get some render-to-texture functionality to work for you - where you would be drawing onto a texture instead of the canvas, and then you could draw the scaled texture - which wouldn't require the extra mem/gfx copy that Czar talks about.


Ranoka(Posted 2011) [#15]
I'm not sure how to approach the render-to-texture approach, but I could do the multiplying the distance by the scale.

My program is still quite small, and the render code is in functions, so shouldn't take too long to adjust.

Thanks, feels like I'm getting somewhere with this.