MT modes explained
SetMTMode temporal multithreading is implemented in AviSynth by a custom cache (cacheMT) and a special filter (Distributor) that distributes work to the different threads. To understand how the different modes work. it is necessary to understand how the normal AviSynth filter chain works. If we have a simple AviSynth script like this:
AviSource("c:\test.avi") Blur(0.7) Subtitle("Test script")
A cache is inserted between each filter to improve performance. The cache is implemented as a filter that stores references to frames generated by the filter above. So the the filter chain looks like the image below (click to enlarge)
When the application that opens the AviSynth script requests a frame the last filter is being invoked first, requesting frames upwards in the filter chain. So in the example above VirtualDub requests the Subtitle cache to deliver a frame. If the frame is not in the cache it requests Subtitle() to generate the frame that again requests the frame from the Blur cache to put the text on. The Blur cache will forward the request to Blur() if the blurred frame is not in the cache. Blur() will request AviSource cache to deliver the frame to Blur() and if the frame is not in the cache AviSource will finally fetch the frame from c:\test.avi and deliver it to AviSource cache.
From there it will be delivered to Blur(), blurred and saved in the Blur cache, delivered to Subtitle() which applies the text "Test script" to a copy of the frame and saves it to the Subtitle cache, and finally be sent to VirtualDub.
mode 1 differs from the above filter chain in that the special filter Distributor is inserted after the last filter in the script (in main.cpp) and that a modified cache (cacheMT1) is used (ExpFunctionCall::InsertCache) that allows more than one thread to enter it safely (so that if one thread is generating the same frame as the next thread needs the second thread will wait for the first thread to complete generating the frame).
The sample script
SetMTmode(1,2) Avisource("c:\test.avi") Blur(0.7) Subtitle("Test script")
produces a filter chain that looks like this (click to enlarge)
Distributor (defined in mt.cpp) creates as many threads as SetMTMode specifies - in this case two threads. Each thread waits for the distributor to assign a frame to work on. The current algorithm that is used for this is rather simple. If distributor is requested to deliver a frame n it will see if the frame is already done, if not it will assign it to the first idle thread that has generated a frame lower than n. If there is more than one idle thread they will be assigned to process the next frame not being currently being generated (so if thread1 is processing frame 10, thread2 is processing frame 11 and thread3 is done processing frame 9, when frame 10 is being requested thread3 would be assigned frame 12 as it is the next one being needed). So currently if the application that opens the AviSynth script doesn't access frames linearly with a delta of 1 frame, there would be a lot of wasted time used on generating frames not needed (although the cache after Distributor will contain some of the frames so it is not entirely wasted).
As seen the two threads can collide in the same instance of the filters used. This can cause problems with filters not designed to allow thread safe access to class variables. To prevent this mode 2 was created:
mode 2 is similar to mode 1 except that instead of one instance per filter being created, there are as many instances created as there are threads (ExpFunctionCall::InsertCache is responsible for creating the filters). Also instead of CacheMT1, CacheMT2 is used (except for the final cache). CacheMT2 is responsible for selecting the filter instance that currently is not in use by one of the other threads (done in CacheMT2::childGetFrame)
The sample script
SetMTMode(2,2) AviSource("c:\test.avi") Blur(0.7) Subtitle("Test script")
produces this filter chain (click to enlarge)
adapted from this page at web.archive.org
← return to MT