IanB
11th July 2004, 12:08
Dear all,
I have been plagued for a long time by random crashes when executing complicated scripts with animate, so this weekend I decided to find it.
Finding the problem exposed an ickyness in the memory managment of Avisynth. I thought it might be worth documenting what I have found.
How the Cache works
New VideoFrameBuffer's when created end up on 2 separate linked lists, 1 globally managed by class "LinkedVideoFrameBuffer" (avisynth.cpp) and another managed by many single instances of class "Cache" (cache.cpp). (I have added "this" (objects address) to all the Cache debug messages to make it more obvious what is happening.)
Each Cache object is automaticly inserted after every filter and add each returned VideoFrameBuffer to a linked list (CachedVideoFrame) in an LRU manner, a stale copy of an oldest frame is reused to hold the current VideoFrameBuffer, if none are stale then a new CachedVideoFrame is created. CachedVideoFrames are never deleted, the VideoFrameBuffer they manage may however become stale.
As each VideoFrameBuffer is initially created it is added by class LinkedVideoFrameBuffer to its linked list also in an LRU manner. Note :- this linked list is NOT updated by using or modifying the VideoFrameBuffer as in "Cache".
VideoFrameBuffer's are newly created until "memory_used" exceeds "memory_max", This is "Plan A". (memory_max is initially the max of 16Mb or 0.25 * Available Memory. It can be modified with SetMemoryMax() between current memory usage and 5MB less than Available Memory)
When "memory_used" exceeds "memory_max", "Plan B" is used for new VideoFrameBuffer's. This involves scanning the list in reverse order for an unlocked buffer exactly the right size. If selected the buffer is moved to the head of the list. Unfortunatly this list is in the order created not order used.
If no unlocked, exact size buffer is found new VideoFrameBuffer's are created, as in "Plan A", this is "Plan D" and is used until "memory_used" exceeds "memory_max" by 25%. When this happens "Plan C" is used, this involves discarding all of the currently unlocked buffers (Effectivly all of the cache).
This is where my problem occured. There is no indication to the "Cache" objects that the data buffer in the VideoFrameBuffer has been released, so with moderatly complicated recursive scripts "Cache" objects attempt to use missing data buffers. By incrementing the "sequence_number" in the destructor ~VideoFrameBuffer, "Cache" now knows its VideoFrameBuffer's are invalid and doesn't crash.
Food for thought
In the current CVS version along with the fix, I have remarked "Plan B" out and changed "Plan C" to only delete enough buffers so that "memory_used" is just less than "memory_max", so the cache floats between 100% and 125% of "memory_max". Initial testing shows a marked improvement in cache performance for complicated scripts and an immeasurable change for simple scripts. -- Some testing please guys.
What is really needed is to let the individual "Cache" objects influence the global "LinkedVideoFrameBuffer" object so true LRU performace can be obtained. I can do it easily with a small change to avisynth.h but because this small change would require all pluggins to be recompiled it is unacceptable.
Anybody have a bright idea of how to hook this together without upsetting avisynth.h?
IanB
I have been plagued for a long time by random crashes when executing complicated scripts with animate, so this weekend I decided to find it.
Finding the problem exposed an ickyness in the memory managment of Avisynth. I thought it might be worth documenting what I have found.
How the Cache works
New VideoFrameBuffer's when created end up on 2 separate linked lists, 1 globally managed by class "LinkedVideoFrameBuffer" (avisynth.cpp) and another managed by many single instances of class "Cache" (cache.cpp). (I have added "this" (objects address) to all the Cache debug messages to make it more obvious what is happening.)
Each Cache object is automaticly inserted after every filter and add each returned VideoFrameBuffer to a linked list (CachedVideoFrame) in an LRU manner, a stale copy of an oldest frame is reused to hold the current VideoFrameBuffer, if none are stale then a new CachedVideoFrame is created. CachedVideoFrames are never deleted, the VideoFrameBuffer they manage may however become stale.
As each VideoFrameBuffer is initially created it is added by class LinkedVideoFrameBuffer to its linked list also in an LRU manner. Note :- this linked list is NOT updated by using or modifying the VideoFrameBuffer as in "Cache".
VideoFrameBuffer's are newly created until "memory_used" exceeds "memory_max", This is "Plan A". (memory_max is initially the max of 16Mb or 0.25 * Available Memory. It can be modified with SetMemoryMax() between current memory usage and 5MB less than Available Memory)
When "memory_used" exceeds "memory_max", "Plan B" is used for new VideoFrameBuffer's. This involves scanning the list in reverse order for an unlocked buffer exactly the right size. If selected the buffer is moved to the head of the list. Unfortunatly this list is in the order created not order used.
If no unlocked, exact size buffer is found new VideoFrameBuffer's are created, as in "Plan A", this is "Plan D" and is used until "memory_used" exceeds "memory_max" by 25%. When this happens "Plan C" is used, this involves discarding all of the currently unlocked buffers (Effectivly all of the cache).
This is where my problem occured. There is no indication to the "Cache" objects that the data buffer in the VideoFrameBuffer has been released, so with moderatly complicated recursive scripts "Cache" objects attempt to use missing data buffers. By incrementing the "sequence_number" in the destructor ~VideoFrameBuffer, "Cache" now knows its VideoFrameBuffer's are invalid and doesn't crash.
Food for thought
In the current CVS version along with the fix, I have remarked "Plan B" out and changed "Plan C" to only delete enough buffers so that "memory_used" is just less than "memory_max", so the cache floats between 100% and 125% of "memory_max". Initial testing shows a marked improvement in cache performance for complicated scripts and an immeasurable change for simple scripts. -- Some testing please guys.
What is really needed is to let the individual "Cache" objects influence the global "LinkedVideoFrameBuffer" object so true LRU performace can be obtained. I can do it easily with a small change to avisynth.h but because this small change would require all pluggins to be recompiled it is unacceptable.
Anybody have a bright idea of how to hook this together without upsetting avisynth.h?
IanB