=====The Ardour memory leak=====
^ **UPDATE:** As of svn revision r4240 (Ardour v2.7.1), the problem seems fixed. ^
\\
Ardour has for a very long time been having a pretty nasty memory leak, somehow connected to its undo history list.\\
The author implemented a way not to store more than a given number of elements in the list, but this did not seem to fix the issue.\\
The problem manifests itself whenever an action is performed, by allocating an amount of memory, growing with number of actions taken to reach the current point in the project. In other words, the longer you work, the more memory one action will take up.\\
The leak is particularly nasty when working on big projects with 16 or more tracks, in which high end computers with as much as 4GB of memory will quickly be sent to its knees.\\
\\
//I have set out to hunt down this malicious bug.//\\
Ardour bug report at [[http://tracker.ardour.org/view.php?id=2444]]
====Analysis====
I started by editing the ''Command'' class in the embedded pbd library, located in the ''libs/pbd/pbd/command.h'' file:
class Command : public PBD::StatefulDestructible
{
public:
Command() { printf("Command: %p\n", this); }
virtual ~Command() { printf("Command: %p (delete)\n", this); }
virtual void operator() () = 0;
virtual void undo() = 0;
virtual void redo() { (*this)(); }
virtual XMLNode &get_state();
virtual int set_state(const XMLNode&) { /* noop */ return 0; }
};
This prints out the pointer to a newly created Command object (and all classes inheriting it), and also prints out its pointer, when it is about to be deleted.\\
This quickly showed me that a lot of these objects are created, but none of the ever deleted again (not even when closing the project).\\
The limiting history functionality was supposed to never make the undolist contain more that a given number of commands (and therefor delete the oldest commands when new ones are put into the list), but it seems that evens setting the limit to 1, doesn't delete any of the ''Command'' objects.
====Test code====
My idea was then to try and delete all the objects when they were inserted into the undo list.\\
This was done by editing the ''Undo'' class, also vacated in the pbd library (''libs/pbd/undo.cc'')
void
UndoTransaction::add_command (Command *const action)
{
delete action;
/* catch death */
// new PBD::ProxyShiva (*action, *this, &command_death);
// actions.push_back (action);
}
This now deletes all ''Command'' objects at the time of insertion into the undo history list, thus totally disabling all undo functionality.\\
I then tested some of my larger projects, and could conclude that my changes indeed did lower the memory allocation problem.\\
I guess the idea of the ''ProxyShiva'' is somehow to delete the ''Command'' object automatically when it is no longer needed, but this is obviously not working as intended.\\
Furthermore I want to point out that there are still ''Command'' objects that are not being deleted (selections for example), and my guess is that these command are not meant to be undoable, but they should definitely still be deleted somewhere.\\
====Measuring====
A test run of the changed code versus the unchanged code with 40 cuts on a 16 track project made the following results:
^ Code ^ mem usage after load ^ mem usage after 40 cuts ^ accumulated memory usage ^
| Unchanged code | 99MB | 217MB | 118MB |
| Changed code | 98MB | 124MB | 26MB |
\\
I repeated the experiment with 80 cuts on the same project:
^ Code ^ mem usage after load ^ mem usage after 80 cuts ^ accumulated memory usage ^
| Unchanged code | 99MB | 542MB | 443MB |
| Changed code | 99MB | 162MB | 63MB |
\\
And again with 120 cuts on the same project:
^ Code ^ mem usage after load ^ mem usage after 120 cuts ^ accumulated memory usage ^
| Unchanged code | 100MB | 994MB | 894MB |
| Changed code | 99MB | 193MB | 94MB |
\\
The results points in the direction of the modified code growing approximately linear in size, and the unmodified code growing almost 2 times faster than linear (of course more measurements must be taken in order to determine the exact curve, but the point is that more cuts, the faster it grows)\\
^ Graph of memory usage ^
| {{ :ardour:ardour_memleak.png }} |
| The red graph is the unmodified code, the blue graph is the modified code. |
\\
//I hope this page might be of some use for the Ardour developers in the continuous work to improve Ardour to become one of the worlds leading DAWs!//\\
\\
//Written by Bent Bisballe Nyeng (deva@aasimon.org)//