Severe CPU rise after having a SDL game open awhile.

Started by
7 comments, last by rip-off 12 years, 9 months ago
Hello,

This is something I just encountered and need some help with...

I have a game I'm working on right now using SDL. Now, when the game starts playing then everything works as planned and programmed, and works at 12-13% of the CPU, at the 100 FPS I set it to, everything is fine.

But, after a few minutes of the game running, it'll start severly slowing down, the FPS drops, and the CPU rises, this goes on until the game now runs at about 5 FPS and 50% of CPU.

The game is frame capped to 100 FPS, like I said, and all SDL_Surfaces in it should be in use, those that aren't are freed.

Since this is a pretty big project, and I don't know which part is relevant, I can't really post it here. So I guess what I am asking is what can make such effects to happen? Are there other important data types I should keep track on freeing?

Thanks alot.
Advertisement

Hello,

This is something I just encountered and need some help with...

I have a game I'm working on right now using SDL. Now, when the game starts playing then everything works as planned and programmed, and works at 12-13% of the CPU, at the 100 FPS I set it to, everything is fine.

But, after a few minutes of the game running, it'll start severly slowing down, the FPS drops, and the CPU rises, this goes on until the game now runs at about 5 FPS and 50% of CPU.

The game is frame capped to 100 FPS, like I said, and all SDL_Surfaces in it should be in use, those that aren't are freed.

Since this is a pretty big project, and I don't know which part is relevant, I can't really post it here. So I guess what I am asking is what can make such effects to happen? Are there other important data types I should keep track on freeing?

Thanks alot.


Hello. This sounds like you are A leaking memory somewhere or B entering a state where your application is being forced to allocate memory all the time. I am not sure if you are on Linux or not but I would suggest running the application through a memory profile application like Valgrind especially if it is a larger project. Most likely there is an issue under certain circumstances where memory is not being freed and it is building up over time slowly. The nice thing about applications like Valgrind is it also tracks the time it takes for each function to execute helping to find bottlenecks as well.
If it is a memory leak you should be able to see that your program takes more and more memory. On windows you can use the Task Manager.
Valgrind seems like a very good solution, but as to my understanding, isn't it Linux only? I'm running on Windows, and I got a little messed up on trying to use it, their quick start manual isn't very good, I'm not sure where or how to install it...
You can determine if it is a massive memory leak by examining the memory columns in the task manager *. The root causes I've seen for massive leaks are accidentally loading images during the game and text rendering functions like TTF_RenderText_*.

Are you creating objects frequently? How do you handle removing them?


How long does it take for this slow down to occur?


* [size="1"]Note: The task manager generally sucks for these kind of things, but here it might be the simplest way to diagnose this as a leak.
EDIT:

Disregard everything below, apparently the trigger point for this was because I was enabling an event that created an object at every run of the game loop, so within seconds the amount of events to handle and sort in the function shown below and various other functions dealing with the events grew from 4 to around 40, and kept growing.

That's the reason for the slowdowns... I'm sorry for wasting your time with this.
I'm gonna leave with 2 questions though;
So first, as I mentioned below about the surface freeing inside that function, is that the right thing to do at all? I mean, as it turns out, not freeing at that location both doesn't raise the memory usage, so I don't think it's being leaked, and do freeing it causes crashing, so should it actually be done there at all, or does it not matter because it's just an iterator or something like that?...
And second, I'd still like some info about Valgrind or something similiar for Windows, it looks like a very useful tool, if anyone could provide that?

Thanks alot for all of your help =)





Original post:



According to the task manager, it rises at about 0.500K memory usage per refresh(Of the data in the task manager).

I'm creating and deleting objects with pictures quite frequently, my code is made in a way that it handles the priority of the sprites(Which need to be blitted first) by first sorting the object list which is a list<Object> , when Object is a class containing all the data relevant to the object, including the SDL_Surface for the sprite.
The sorting function is run at every running of the game loop, since priority is something that is changed all the time.
Inside that sorting function, I need to delete the old objects from their original location after they are put in the new position in the list, so that they don't duplicate.

Now, there are a few things that happen here, that I find contradicting... maybe you can help clarify this...

This is the code for the sorting function.


list<Event> sortEvents(list<Event> &evList, Chara &ch){
//First, check if the list isn't empty
if(evList.empty())
return evList;
//Then, sort the events,
Event e1 = evList.front();
Event e2 = evList.back();
ch.drawPos = -1;
//If the list only has 1 item, then it doesn't need to be
//sorted, but still needs to set the hero's drawPos.
if(e1.index == e2.index){
if(ch.y > e1.y) ch.drawPos = 1;
else ch.drawPos = 0;
return evList;
}
list<Event>::iterator it = evList.begin();
list<Event>::iterator it2 = evList.end();
it2 = it;
list<Event> overList;
list<Event> underList;
underList.clear();
overList.clear();

while(it != evList.end()){
if(it->solid == 1){
underList.push_back(it);
evList.erase(it);
it = evList.begin();
it2 = it;
}
else if(it->solid == 2){
overList.push_back(it);
evList.erase(it);
it = evList.begin();
it2 = it;
}
else{
it2 = it;
it++;
}
}


//Checking again if the list only has 1 item, then it doesn't
//need to be sorted, but still needs to set the hero's drawPos.
if(!evList.empty()){
it = evList.begin();
it2 = it;
it2++;
e1 = evList.front();
e2 = evList.back();
ch.drawPos = -1;
if(e1.index == e2.index){
if(ch.y > e1.y) ch.drawPos = 1;
else ch.drawPos = 0;
}
for(int i = 0; it2 != evList.end(); i++){
if(it->y > it2->y){
evList.insert(it, it2);
evList.erase(it2);
it = evList.begin();
it2 = it;
++it2;
i = 0;
}
else{
if(ch.y >= it->y && ch.y <= it2->y)
ch.drawPos = i+1;
else if(ch.y > it2->y)
ch.drawPos = i+2;
if(it != evList.end())
++it;
++it2;
}
}
}
else
ch.drawPos = 0;


it2 = overList.begin();
while(it2 != overList.end()){
evList.push_back(it2);
it2++;
}
it2 = underList.begin();
while(it2 != underList.end()){
evList.push_front(it2);
ch.drawPos++;
it2++;
}
/*it = evList.begin();
for(int q = 0; q < ch.drawPos; q++)
++it;
while(it != evList.end() && (it->solid == 1 || it->solid == 3)){
ch.drawPos++;
++it;
}*/
return evList;
}


A few clarifications about this code:
-The drawing of the events is done in a for loop that goes through the evList, Chara::drawPos is an indicator of the position in the loop where it should draw the hero's sprite.
-the Event::solid variable is a pre-set value determining if the event has auto-priority, 0 being default, 1 is always below(Hence goes in the underList, and then attached back to the bottom of the evList) and 2 being always above everything else(Attached to overList, then to the top of evList).

Now, the things I find odd;
-The drop doesn't happen right off the bat, it only does after a certain event in the game is triggered, I'm not yet sure what it is, but I'll get back to you on it.
-As you can see, this function doesn't have code to free the sprite of the events it deletes with evList.erase(it).
I used to have a SDL_FreeSurface(it->pic); right before each evList.erase(it);, but that didn't work, after a few runs it'll complain about something with the heap that "Might've pressed F12 while program had focus" message that doesn't really give any info...

So basicly, it's probably not freed at that point, but I can't free it at that point either... so I'm not sure where I'm going wrong really...


Also, you asked how long it takes it to drop in speed, so, before I hit the aforementioned point that triggers the drop, it doesn't drop at all, but after that, it's a matter of seconds before it's like 0.1 the speed it's supposed to be...

I'm sorry if this is a little cluttered, I'm just not really sure what I'm doing here...
That code looks horrendously complex. Consider std::sort()*:

struct EventComparison
{
bool operator<(const Event &a, const Event &b) const
{
// This would be nicer if you redefined "solid" such that under = 0, default = 1 and over = 2
static const lookup[] = {1, 0, 2};
return lookup[a.solid] < lookup[b.solid];
}
};

void sortEvents(list<Event> &evList, Chara &ch)
{
evList.sort(EventComparison());
// Other logic (setting drawPos?)
}

I'm suspicious that Events appear to know their own index. Does Event allocate any memory? Does it handle the rule of three without leaking?

* [size="1"]I'm using the member function std::list::sort here. Unless there is a reason for using std::list I think you should consider using std::vector instead, with the free function std::sort(begin, end).
Well, yea, I'm using the rule of two actually, there's no destructor since all the values except the SDL_Surface for the sprite are simple integers and booleans, and like I said, I tried making a destructor for that, but it keeps dropping the unexplained error about F12 and whatnot...

The problem with using the std::sort is that most of the complexity is derived from the need to find when the list is empty, has no prioritized members and allocate the drawPos, so, atleast when I think about it at this level, it doesn't seem that it'll look much less complex when I apply these, and for now it works, so I guess it's ok.
Also, I'm not sure I understood how your example is handled in a loop to go through all the members?

As for lists vs. vectors, I think lists are more appropriate for me, since the indexes, as you see, aren't linear, they are sorted by position on the screen, so the [] operator doesn't really help me, since the index of the Event is irrelevant to its' position in the list and needs to be sought for either way, and like you see here, and it's the same in alot of other places in the code too, there's constant need to add and remove objects, which, to my understanding, a list does better.
Rule of two? Can you post your event class? You're probably leaking something if you're storing them in an SDL container without sensible copy semantics.

Why are empty lists or 1 element lists special cases? If you formulate your algorithm correctly this should be an issue. Specifically, I hope you're not trying to "optimise" the cases that are going to be the fastest anyway!

As for lists vs. vectors, I think lists are more appropriate for me, since the indexes, as you see, aren't linear, they are sorted by position on the screen, so the [] operator doesn't really help me, since the index of the Event is irrelevant to its' position in the list and needs to be sought for either way, and like you see here, and it's the same in alot of other places in the code too, there's constant need to add and remove objects, which, to my understanding, a list does better.
[/quote]
std::vector is cache friendly and can be faster for adding/removing new elements, if you use the "swap and pop" trick or the "remove/erase" idiom. std::list is expensive due to the dynamic allocations it requires.

Here is my attempt to cleanup your code into simpler algorithms. I did this in two passes. The first was a simple cleanup where I tried to figure out what each loop was doing, and separated the loops from the others by removing shared variables. I also tried to clarify certain situations, like where you are testing for a single element list.

This was the result of the first pass:

#include <list>

struct Event
{
int y;
int index;
int solid;
};

struct Character
{
int y;
int drawPos;
};

typedef std::list<Event> EventList;
typedef EventList::const_iterator EventListIterator;

EventList sortEvents(EventList &evList, Character &hero)
{
if(evList.empty())
{
return evList;
}

if(evList.size() == 1)
{
Event event = evList.front();
if(hero.y > event.y)
{
hero.drawPos = 1;
}
else
{
hero.drawPos = 0;
}
return evList;
}

EventList overList;
EventList underList;

for(EventListIterator it = evList.begin() ; it != evList.end() ; /* ... */)
{
if(it->solid == 1)
{
underList.push_back(*it);
evList.erase(it);
it = evList.begin();
}
else if(it->solid == 2)
{
overList.push_back(*it);
evList.erase(it);
it = evList.begin();
}
else
{
it++;
}
}

if(evList.empty())
{
hero.drawPos = 0;
}
else
{
if(evList.size() == 1)
{
const Event &event = evList.front();

if(hero.y > event.y)
{
hero.drawPos = 1;
}
else
{
hero.drawPos = 0;
}
}
else
{
hero.drawPos = -1;
}

EventListIterator it1 = evList.begin();
EventListIterator it2 = it1;
it2++;

for(int i = 0; it2 != evList.end(); i++)
{
if(it1->y > it2->y)
{
evList.insert(it1, it2);
evList.erase(it2);
it1 = evList.begin();
it2 = it1;
i = 0;
}
else
{
if(hero.y >= it1->y && hero.y <= it2->y)
{
hero.drawPos = i + 1;
}
else if(hero.y > it2->y)
{
hero.drawPos = i + 2;
}

if(it1 != evList.end())
{
++it1;
}

}
++it2;
}

}

for(EventListIterator it = overList.begin() ; it != overList.end() ; ++it)
{
evList.push_back(*it);
}

for(EventListIterator it = underList.begin() ; it != underList.end() ; ++it)
{
evList.push_front(*it);
hero.drawPos++;
}

return evList;
}


Using this as a base, I tried to decouple the two things you were doing in this function. One was sorting the event list, and the other was determining the hero's draw position.

I replaced the hand written logic with standard library calls. This is a pre C++0x version.

#include <list>
#include <iterator>
#include <algorithm>

struct Event
{
int y;
int index;
int solid;
};

struct Character
{
int y;
int drawPos;
};

typedef std::list<Event> EventList;
typedef EventList::const_iterator EventListIterator;

struct EventSorter
{
bool operator()(const Event &a, const Event &b) const
{
if(a.solid == b.solid)
{
return a.y < b.y;
}
static const int lookup[] = {1, 0, 2};
return lookup[a.solid] < lookup[b.solid];
}
};

void doSortEvents(EventList &events)
{
events.sort(EventSorter());
}

struct HeroDrawPositionLocator
{
public:
HeroDrawPositionLocator(Character &hero)
:
hero(&hero)
{
}

bool operator()(const Event &event) const
{
if(event.solid != 0)
{
return false;
}
return event.y > hero->y;
}
private:
Character *hero;
};

void setHeroDrawPosition(const EventList &events, Character &hero)
{
EventListIterator it = std::find_if(events.begin(), events.end(), HeroDrawPositionLocator(hero));

if(it != events.end())
{
int i = std::distance(events.begin(), it);
hero.drawPos = i + 1;
}
else
{
hero.drawPos = -1;
}
}

void sortEvents(EventList &events, Character &hero)
{
doSortEvents(events);
setHeroDrawPosition(events, hero);
}


In C++0x (available in modern compilers such as MS Visual C++ 2010), we can use lambda functions to inline the searching/sorting predicates:

#include <list>
#include <iterator>
#include <algorithm>

struct Event
{
int y;
int index;
int solid;
};

struct Character
{
int y;
int drawPos;
};

typedef std::list<Event> EventList;
typedef EventList::const_iterator EventListIterator;

void doSortEvents(EventList &events)
{
events.sort([](const Event &a, const Event &b) -> bool {
if(a.solid == b.solid)
{
return a.y < b.y;
}
static const int lookup[] = {1, 0, 2};
return lookup[a.solid] < lookup[b.solid];
});
}

void setHeroDrawPosition(const EventList &events, Character &hero)
{
EventListIterator it = std::find_if(events.begin(), events.end(), [&hero](const Event &event) -> bool {
if(event.solid != 0)
{
return false;
}
return event.y > hero.y;
});

if(it != events.end())
{
int i = std::distance(events.begin(), it);
hero.drawPos = i + 1;
}
else
{
hero.drawPos = -1;
}
}

void sortEvents(EventList &events, Character &hero)
{
doSortEvents(events);
setHeroDrawPosition(events, hero);
}

They say most programs spend all their time searching and sorting. It is a good idea to separate the search/sort "predicate" (the logic which determines if you've found the right object, or if something is out of order) from the logic which performs the search/sort itself.

Apart from the shorter, more readable code, another advantage is performance. Your custom sort loop is pretty much the worst way to perform a sort. It resulted in lots (and lots) of unnecessary work. Your game was probably slowing down very quickly simply due to the poor algorithm in use, and also the excessive dynamic allocation involved.

I may have made some mistakes during this process, so were I you I'd write some unit tests for the functions to ensure they act correctly under different circumstances.

This topic is closed to new replies.

Advertisement