Destruction of Objects with Cyclic References: MATLAB automated procedure is much too slow, how to turn it off (and reimplement a solution fitted to my needs by writing proper destructor methods)?

7 views (last 30 days)
I need to construct a lot of instances of classes that are interconnected, where properties of the objects contain handles of other objects. When Matlab wants to delete these objects, it takes a huge amount of time (seems to be quadratic or even worse). It is due to the fact that the Handle Class Destructor in Matlab features a particular procedure for “Destruction of Objects with Cyclic References”:
From "Handle Class Destructor" documentation: Consider a set of objects that reference other objects of the set such that the references form a cyclic graph. In this case, MATLAB:
  • destroys the objects if they are referenced only within the cycle
  • does not destroy the objects as long as there is an external reference to any of the objects from a MATLAB variable outside the cycle
The problem is that this method causes these terribly slow performances: For each object, the procedure has to find those cyclic dependencies, which takes a lot of time.
I found a first solution to the problem: I just tell Matlab that when one of these objects has to be deleted, we can be sure that we can delete all the referenced objects as well. No need to check for cycle. For that, in the destructor of a given class, I call the destructor of all the referenced objects. This works in simple cases with not many objects, the garbage collection is much faster. But there is a hiccup: all these recursive calls to “delete” leads to stack overflow when the number of objects is larger.
So the optimal solution, in my sense, would be to toggle off the automated deletion of objects with cyclic references and to perform all the deletion manually (by writing the destructor of each class appropriately). But how to toggle off the automated deletion of objects with cyclic references? That is the question...
Any thought?
  7 Comments
Olivier Lartillot
Olivier Lartillot on 6 May 2016
Sure.
  • Download a long MIDI file. For instance this one.
  • Choose as Current Directory the one containing that MIDI file.
  • Simply write the command:
mus.score('mazrka17.mid') % mazrka17.mid is the name of the MIDI file
This command loads the MIDI file and displays the notes in a Figure. It might take around 20 s to do this. There is surely way to write a faster code, I should have a look, but this is not the problem here.
The problem is now if we want to destroy the objects. For instance by calling “clear”. It will take ages.
You can see the process of the destruction of objects by adding for instance the following method to pat.event:
function delete(obj)
obj
end
You see the notes in the MIDI files progressively removed from the memory. It is very slow at the beginning and gets progressively faster. Indeed, there are tons of cycles that Matlab wants to check, and progressively less and less afterwards.
David Foti
David Foti on 23 Feb 2017
I know it has been a while and my apologies for the long time since this question was posted, but I believe that R2016b MATLAB contains a fix for the slow destruction with this newer version. I now see the clear taking about 2 seconds vs. hundreds before.
The issue was in bookkeeping during destruction of a graph when all nodes are dead (when the last variable is to the root node is cleared). When MATLAB is unable to find a parent graph node that is still reachable by a MATLAB variable, it remembers that all nodes along all paths searched from the starting node are also unreachable so it should not try to traverse those same paths again. However, an interaction with MATLAB's shared data mechanism meant that this cache was getting cleared when one of the nodes was an array that was referenced from two different places (MATLAB makes actual copies of arrays only when the array is changed and otherwise shares the data). In R2016b, the condition that causes the cached information to be invalidated was tightened.

Sign in to comment.

Answers (2)

Philip Borghesani
Philip Borghesani on 6 May 2016
There is no way to toggle off the detection algorithms, doing so would cause enough objects to leak that MATLAB would probably become unusable in a short time.
  1 Comment
Olivier Lartillot
Olivier Lartillot on 6 May 2016
Edited: Olivier Lartillot on 7 May 2016
Maybe a way to declare, in the destructor of a class, that a given object referenced by the current object can be destroyed (so that the detection algorithm does not need to be launched here)?
Or a new property attribute maybe?

Sign in to comment.


David Foti
David Foti on 9 May 2016
Hi Olivier,
Thank you for providing instructions to see the issue. Based on the way the seq.Sequence is being used, I wonder if you intended it to be a handle class? It appears that every event has a sequence stored in a property. This sequence will be a copy of the object passed to the event constructor with however many events have been added to its contents at the time the event is created. In your example, this leads to a huge number of copies of objects that each reference a potentially huge number of events. I can't find usage in the class files that indicates a need for each event to have its own copy of the sequence contents and the call to the integrate method on line 42 of seq.event doesn't capture the updated sequence object which suggests to me a possible expectation that sequence behaves like a handle and is modified by the integrate method.
If I change seq.Sequence to subclass handle, clearing the workspace takes about 6 seconds for me. Does it make sense for seq.Sequence to be a handle? If not, is there a way for events to avoid needing a copy of the sequence? Could they instead store only the information they need?
  2 Comments
Olivier Lartillot
Olivier Lartillot on 9 May 2016
Edited: Olivier Lartillot on 9 May 2016
Hi David,
Thanks a lot for your careful look at the code. This is much appreciated! Your suggestion is very relevant. Indeed, I should have declared seq.Sequence as a handle class. This correction speeds up the garbage collection, but it can be still long for more demanding analyses, where many other objects are constructed on top of the initial graph.
For instance:
mus.score('mazrka17.mid','Group')
or even more demanding:
mus.score('mazrka17.mid','Group','Motif')
(The last command takes too much time to perform for this MIDI file. But anyway, we can interrupt at any time, and then see the garbage collection crunching.)
Olivier Lartillot
Olivier Lartillot on 11 May 2016
Edited: Olivier Lartillot on 11 May 2016
In fact, using the version of the library mentioned in the previous link, by declaring seq.Sequence as a handle class, it now takes only a few seconds to cleanup, compared to the very long time for the actual analysis.
But actually the problem still persists with the most recent version of my code.
When running this command:
mus.score('mazrka17.mid','Motif','Notes',1:500); %500 first notes analysed
the cleanup takes 37 seconds
With this command:
mus.score('mazrka17.mid','Motif','Notes',1:1000); %1000 first notes analysed
the cleanup takes 437 seconds
Here is below more information about the number of objects created in each case:
  • 100 notes:cleanup: 0.291125 secondsseq.Sequence = 2seq.event = 100pat.event = 100seq.syntagm = 99pat.pattern = 65pat.occurrence = 311pat.echo = 132pat.cycle = 2pat.memory = 624pat.memostruct = 78pat.memoparam = 546
  • 500 notes:cleanup: 37.521583 secondsseq.Sequence = 2seq.event = 500pat.event = 500seq.syntagm = 499pat.pattern = 318pat.occurrence = 2878pat.echo = 1781pat.cycle = 29pat.memory = 2744pat.memostruct = 343pat.memoparam = 2401
  • 1000 notes:cleanup: 436.805551 secondsseq.Sequence = 2seq.event = 1000pat.event = 1000seq.syntagm = 999pat.pattern = 784pat.occurrence = 7967pat.echo = 5569pat.cycle = 71pat.memory = 6768pat.memostruct = 846pat.memoparam = 5922
This problem seems to be due to fact that the objects of the classes have a lot of links to other objects, so it creates a lot of cycles during the cleanup.

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!