AvsTimer 0.5

An Avisynth 2.5x plugin for measuring speed and performance of Avisynth plugins

By Rainer Wittmann gorw@gmx.de

The binary and the source code are subject to the GNU General Public License. Last change March 7, 2004.

Installation

Beginning with version 0.4.1 the binary package of AvsTimer contains two versions of the plugin. AvsTimer.dll (dynamically linked, hence small) and AvsTimerS.dll (staticly linked, hence big). Try first the small dll and copy it to Avisynth's plugin directory. If it doesn't work, this is probably due to missing msvcr70.dll library. Either you install this library in C:\windows\system32 (but not in each application directory), or you delete AvsTimer.dll and replace it by AvsTimerS.dll. Please do not put both dlls into the plugin directory. If you fill the plugin directory with all kinds of superfluous dlls, then you only slow down the start of any application which uses Avisynth.

Usage

AvsTimer  is an Avisynth filter (for Avisynth 2.5 or higher), which actually doesn´t make any changes to a clip. In fact, it even doesn´t make a simple bitblt operation. Thus its overhead is virtually zero. Rather it is a utility to measure the performance of various filters, decoders etc. and should be particular useful for filter writers who want to tweak their filters for maximum speed. To use it one needs the debugview utility or any other program which can catch and display debug messages. Debugview is also used by various other Avisynth filters. Thus it is a utility which a serious Avisynth user should have anyway. Let´s begin with a simple Avisynth script:  
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\decomb.dll")
MPEG2Source("input.d2v")
crop(16,4,692,568)
FieldDeinterlace()
If we now insert the two lines,
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\decomb.dll")
LoadPlugin("C:\Programme\avisynth\plugins\AvsTimer.dll") 
MPEG2Source("input.d2v")
AvsTimer() 
crop(16,4,692,568)
FieldDeinterlace()
load the script into VirtualDubMod and start preview, debugview displays the following output:
[1748] AvsTimer 0.3
[1748] AvsTimer 0.3
[1748] AvsTimer 0.3
[1748] [1999] Timer1 = 69 fps
[1748] [3999] Timer1 = 67 fps
[1748] [5999] Timer1 = 66 fps
[1748] [7999] Timer1 = 65 fps
[1748] [9999] Timer1 = 69 fps
The first number in brackets is the process number of VirtualDubMod. This number is useful, if more than one program is sending debug messages. The second number in brackets is the frame number, at which AvsTimer displays the information. In our example, AvsTimer displays the timer results every two thousand frames. This is the default. We can change the default by using AvsTimer with the option frames. If we insert AvsTimer(frames= 1000) into the script, then the results are displayed every thousand frames. The larger the number of frames the more accurate are the results. Thus it is not a good idea at all to use AvsTimer(frames=1). Perhaps one may then get the error message "zero or negative ticks", because the time was too short to be measurable. If a number <=0 is specified for the frames variable, then frames is simply set to the number of frames of the entire clip. Thus in this case the frame rate is only displayed once after the entire clip has been processed. The first string "Timer1" is the name of the timer. If AvsTimer is inserted three times into the script, then one gets also results from "Timer2" and "Timer3". Instead of these default names, one my specify arbitrary names as an option. If we replace AvsTimer() by AvsTimer(frames=1000, name="mpeg2dec"), then we get the following output from debugview:
[1864] AvsTimer 0.3
[1864] AvsTimer 0.3
[1864] AvsTimer 0.3
[1864] [999] mpeg2dec = 68 fps
[1864] [1999] mpeg2dec = 68 fps
[1864] [2999] mpeg2dec = 68 fps
[1864] [3999] mpeg2dec = 67 fps
[1864] [4999] mpeg2dec = 66 fps
[1864] [5999] mpeg2dec = 67 fps
The value after = is the number of frames per seconds which the filter chain before "mpeg2dec" in the Avisynth script would have, if it would run without the filter chain after "mpeg2dec" and VirtualDubMod. All frame rates are averaged over the last 2000 frames (or the the number specified with the option frames ) and are always rounded up or downwards to the next integer. For instance, the script
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\decomb.dll")
LoadPlugin("C:\Programme\avisynth\plugins\AvsTimer.dll") 
MPEG2Source("input.d2v")
AvsTimer(frames=1000, name="mpeg2dec") 
crop(16,4,692,568)
FieldDeinterlace()
AvsTimer(frames=1500)
gives the following output:
[1908] AvsTimer 0.3
[1908] AvsTimer 0.3
[1908] AvsTimer 0.3
[1908] [999] mpeg2dec = 68 fps
[1908] [1499] Timer2 = 27 fps
[1908] [1999] mpeg2dec = 69 fps
[1908] [2999] mpeg2dec = 66 fps
[1908] [2999] Timer2 = 27 fps
[1908] [3999] mpeg2dec = 69 fps
[1908] [4499] Timer2 = 27 fps
[1908] [4999] mpeg2dec = 67 fps
[1908] [5999] mpeg2dec = 65 fps
[1908] [5999] Timer2 = 27 fps
Unlike "mpeg2dec" "Timer2" now includes the deinterlacer FieldDeinterlace. That´s why the frame rate is now much smaller. One can also measure the frame rate of the filter chain between two calls of AvsTimer. For this purpose we have the "difference" and the "quiet" option. Changing the script as follows: 
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\decomb.dll")
LoadPlugin("C:\Programme\avisynth\plugins\AvsTimer.dll") 
MPEG2Source("input.d2v")
AvsTimer(frames=1000, name="mpeg2dec", quiet=true) 
crop(16,4,692,568)
FieldDeinterlace()
AvsTimer(frames=1500, name="decomb", difference=1)
we get the following output
[1952] AvsTimer 0.3
[1952] AvsTimer 0.3
[1952] AvsTimer 0.3
[1952] [1499] decomb = 45 fps
[1952] [2999] decomb = 46 fps
[1952] [4499] decomb = 46 fps
[1952] [5999] decomb = 45 fps
[1952] [7499] decomb = 45 fps
[1952] [8999] decomb = 46 fps
The displayed frame rate  is now just the frame rate of the filter chain from crop(...) to FieldDeinterlace() (crop really doesn´t matter as it is a "zero cost" filter). Replacing Decomb with TomsMoComp we get the following results:
[432] AvsTimer 0.3
[432] AvsTimer 0.3
[432] AvsTimer 0.3
[432] [1499] TomsMoComp = 97 fps
[432] [2999] TomsMoComp = 98 fps
[432] [4499] TomsMoComp = 98 fps
[432] [5999] TomsMoComp = 98 fps
[432] [7499] TomsMoComp = 98 fps
[432] [8999] TomsMoComp = 97 fps
If  difference=k is specified for the m-th timer then the filter chain between the m-th and the (m-k)-th timer is taken. If m-k<= 0 then Avisynth terminates with an error message. In the above example we have suppressed the output of "mpeg2dec" with the option "quiet= true". Without this option we would have gotten the same "mpeg2dec" output as before. Instead of taking the average only over the last 2000 frames or whatever has been specified with the frames option, one may average over all frames, which have been processed so far, by setting the boolean variable accumulate  to true. Thus the script
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\TomsMoComp.dll")
LoadPlugin("C:\Programme\avisynth\plugins\AvsTimer.dll") 
MPEG2Source("input.d2v")
AvsTimer(frames=1000, name="mpeg2dec", accumulate=true) 
crop(16,4,692,568)
TomsMoComp(1, 5, 0)
AvsTimer(frames=1500, name="TomsMoComp" , difference=1)
gives the following results
[652] AvsTimer 0.3
[652] AvsTimer 0.3
[652] AvsTimer 0.3
[652] [999] mpeg2dec = 68 fps
[652] [1499] TomsMoComp = 96 fps
[652] [1999] mpeg2dec = 69 fps
[652] [2999] mpeg2dec = 68 fps
[652] [2999] TomsMoComp = 97 fps
[652] [3999] mpeg2dec = 68 fps
[652] [4499] TomsMoComp = 97 fps
[652] [4999] mpeg2dec = 68 fps
[652] [5999] mpeg2dec = 68 fps
[652] [5999] TomsMoComp = 98 fps
[652] [6999] mpeg2dec = 67 fps
[652] [7499] TomsMoComp = 98 fps
[652] [7999] mpeg2dec = 67 fps
[652] [8999] mpeg2dec = 68 fps
[652] [8999] TomsMoComp = 98 fps
[652] [9999] mpeg2dec = 68 fps
[652] [10499] TomsMoComp = 98 fps
[652] [10999] mpeg2dec = 68 fps
[652] [11999] mpeg2dec = 68 fps
[652] [11999] TomsMoComp = 98 fps
[652] [12999] mpeg2dec = 68 fps
[652] [13499] TomsMoComp = 97 fps
[652] [13999] mpeg2dec = 68 fps
[652] [14999] mpeg2dec = 68 fps
[652] [14999] TomsMoComp = 97 fps
In this example the "mpeg2dec" timer averages over all previous frames while "TomsMoComp" timer averages only over the last 1500 frames. Consequently the frame rates for "mpeg2dec" stabilise with more and more frames processed, while the frame rates for "TomsMoComp" continue to fluctuate. AvsTimer comes with four methods for measuring time. The default method type=2 is based on the windows functions GetThreadTimes, which measures the time the filter chain spends within the thread, by which is it is called. This is the most accurate method as long as no other threads are used by the filter chain. Currently I know of no example which violates this assumption. However, as hyperthreading becomes more and more fashionable times may change. If one uses the option type=1, then time is measured with GetProcessTimes. Then the time of the process, which runs the Avisynth script, is measured, while the process is executing the filter chain. However, some threads of this process, which have nothing to do with the filters, but run parallel may artificially reduce the frame rate. If type=0 or type=3 is used, then instead of process or thread time the absolute time is taken. Then also other processes may influence the frame rates to the downside. If  type=0 then the function QueryPerformaceCounter is used, if type=3 then the cpu instruction RDTSC is used. type=3 has certainly the smallest overhead, but during initialisation 1 second is necessary to determine the cpu frequency and on some notebooks the cpu frequency may change over time obliterating all the results. To avoid the delay one may also specify the cpu frequency with the frequency option. This must happen with the first type=3 timer or any other timer befor the first type=3 timer otherwise the frequency option is ignored. Thus AvsTimer(type=3, frequency= 1300) defines a type= 3 timer for a cpu with 1300 MHZ. One can get the exact cpu frequency by running a type= 3 timer without the frequency option. Then the exact cpu frequency is displayed in the debugview window. Timers, which are paired together by the difference option, shouldbe of the same type, otherwise the results do not make sense and may even yield negative frame rates. Timers of type!= 2 have an additional advantage. With such timers the frame rate of the entire process running the script  - we call this the total frame rate - can be measured and is displayed by default. Thus the script
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\decomb.dll")
LoadPlugin("C:\Programme\avisynth\plugins\AvsTimer.dll") 
MPEG2Source("input.d2v")
AvsTimer(frames=1000, name="mpeg2dec", type=3) 
crop(16,4,692,568)
FieldDeinterlace()
AvsTimer(frames=1500, name="decomb", type=3, difference=1)
gives following output
[1252] AvsTimer 0.3
[1252] AvsTimer 0.3
[1252] AvsTimer 0.3
[1252] CPU frequency = 1312 MHZ
[1252] [999] mpeg2dec = 68 fps, total = 17 fps
[1252] [1499] decomb = 45 fps, total = 19 fps
[1252] [1999] mpeg2dec = 68 fps, total = 26 fps
[1252] [2999] mpeg2dec = 67 fps, total = 26 fps
[1252] [2999] decomb = 46 fps, total = 25 fps
[1252] [3999] mpeg2dec = 67 fps, total = 26 fps
[1252] [4499] decomb = 46 fps, total = 25 fps
[1252] [4999] mpeg2dec = 67 fps, total = 25 fps
[1252] [5999] mpeg2dec = 65 fps, total = 25 fps
[1252] [5999] decomb = 45 fps, total = 25 fps
[1252] [6999] mpeg2dec = 66 fps, total = 25 fps
[1252] [7499] decomb = 46 fps, total = 25 fps
The output of the total frame rate can be disabled by using the total=false option. It is notable from the above output, that the first total frame rate of each timer is substantially lower than the subsequent ones. This is because the "total" clock of each timer is started, when the filter is created by Avisynth. Because the timers are of type=3 even the time until preview is started in VirtualDubMod is counted. Thus the first total frame rates should be ignored if timers of type=0 or 3 are used or one should use GetAll, which is described below. From the above tests we see that TomsMoComp with 98 fps is much faster than FieldDeinterlace with 45 fps which  is even significantly slower than the hard working mpeg2dec3 with 66 fps. All test were run on a 1.3 GHZ Celeron (Tualatin P3 design) with AvsTimer 0.3 .

AvsTimer and the Avisynth Cache

In order to avoid reprocessing of frames, Avisynth has a cache for each instance of a filter in a script. This cache clearly enhances performance, if time consuming filters which operate on multiple frames are used. AvsTimer allows you to monitor the efficiency of these caches. In fact, in most cases, is able to replace the  cache of the filter immediately before AvsTimer and replaces it by its own cache without any measurable performance penalty. For instance in the script
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\decomb.dll")
LoadPlugin("C:\Programme\avisynth\plugins\AvsTimer.dll")
MPEG2Source("input.d2v")
AvsTimer(frames=1000, name="mpeg2dec", type=3)
crop(16,4,692,568)
FieldDeinterlace()
AvsTimer(frames=1500, name="decomb", type=3, difference=1)
the first AvsTimer instance overtakes the cache of MPEG2Source and the second overtakes the cache of  FieldDeinterlace. What is this overtaking good for? If the frame difference between two output lines of an AvsTimer instance is exactly the value of the frames variable, then there was no cache miss for the filter taken over by this instance of AvsTimer. More precisely we have the formula

number of cache misses = frames - (frame difference of two subsequent output lines)

To be more concrete we look at the output of this script in the previous section. The frame counts of the instant of AvsTimer with name "mpeg2dec" are 999,1999,2999, 3999,4999,5999,6999. Thus the frame difference is always 1000, which is exactly the value of the frames variable. Thus there was no cache miss for the important clip generator MPEG2Source. Similarily, The frame counts of the instant of AvsTimer with name "decomb" are 1499,2999,4499,5999,7499. Thus the frame difference is always 1500, which is exactly the value of the frames variable of this AvsTimer instance. Hence there was also no cache miss for the filter FieldDeinterlace. Though the cache resources are quite limited (they depend on the resolution and the color space of the frame) and the Avisynth is far from optimal it works perfectly for small scripts like the above. However, in scripts with many sophisticated filters substantial cache misses are inevitable and AvsTimer should measure these accurately. To get optimal throughput of scripts with many filter, each filter should have an option to disable its cache. In this way caching could be restricted to the most important filters in order to maximise performance, which can be monitored by AvsTimers. Unfortunately the Avisynth filter API does not provide such a function. Instead it provides function to mnaipulate the cache of the child filter (and all my filters will now make ample use of it). This is ideal for configuring the optimal interaction of the various filters, but somewhat bizarre from the point of view of the script writer. Nevertheless, any filter should have the option cache with values true and false (true is of course the default), to disable caching of those filters which are used as input. If any filter would have this option, then big scripts could be optimised. This is the reason, why from now on all my filters will have this option. It is not mentioned in the documentation of these filters, though, because it has always the same functionality. For instance, RemoveDirt(cache= false) will disable input caching of this filter (only available since version 0.6).

The GetAll Filter

As already  mentioned in the preceding section other programs or threads may influence the results if type!= 2.  Thus for performance tests a simple program, which would only request one frame after the other from Avisynth and do nothing else, would be ideal. It would even be better as far as accuracy is concerned to have a filter with such a property. That´s what GetAll is all about.  If VirtualDubMod or any other program running the Avisynth script is requesting the trigger frame (by default this is the first frame and has the frame number 0) from Avisynth and GetAll is in the filter chain, then GetAll request all other frames before it returns the trigger frame to the calling program which then has to wait for quite a while. All frames may be a little to much. With GetAll(frames=10000) GetAll will only request the first 10000 frames. Modifying our example script as follows
LoadPlugin("C:\Programme\avisynth\plugins\MPEG2Dec3.dll") 
LoadPlugin("C:\Programme\avisynth\plugins\decomb.dll")
LoadPlugin("C:\Programme\avisynth\plugins\AvsTimer.dll") 
MPEG2Source("input.d2v")
AvsTimer(frames=1000, name="mpeg2dec", type=0) 
crop(16,4,692,568)
FieldDeinterlace()
AvsTimer(frames=1500, name="decomb", difference=1, type=0)
GetAll(frames=10000)
we get the following output
[2000] AvsTimer 0.3
[2000] AvsTimer 0.3
[2000] AvsTimer 0.3
[2000] [999] mpeg2dec = 66 fps, total = 25 fps
[2000] [1499] decomb = 44 fps, total = 26 fps
[2000] [1999] mpeg2dec = 67 fps, total = 27 fps
[2000] [2999] mpeg2dec = 66 fps, total = 27 fps
[2000] [2999] decomb = 46 fps, total = 27 fps
[2000] [3999] mpeg2dec = 63 fps, total = 26 fps
[2000] [4499] decomb = 45 fps, total = 26 fps
[2000] [4999] mpeg2dec = 65 fps, total = 26 fps
[2000] [5999] mpeg2dec = 64 fps, total = 26 fps
[2000] [5999] decomb = 45 fps, total = 26 fps
[2000] [6999] mpeg2dec = 65 fps, total = 26 fps
[2000] [7499] decomb = 45 fps, total = 26 fps
[2000] [7999] mpeg2dec = 65 fps, total = 27 fps
[2000] [8999] mpeg2dec = 68 fps, total = 27 fps
[2000] [8999] decomb = 46 fps, total = 27 fps
[2000] [9999] mpeg2dec = 67 fps, total = 27 fps
Now it is no more necessary to start preview, because by requesting frame 0, VirtualDubMod triggers GetAll to request the first 10000 frames. Only after it has completed these 10000 frames the frame 0 is delivered to VirtualDubMod. The disadvantage of GetAll, of course, is that it cannot be used during a normal encoding process.  To get trustworthy frame rates with timers of type!=2 insert GetAll immediately after the last AvsTimer call, shut down all cpu intensive processes and run the script in VirtualDubMod to trigger GetAll.

The Shutdown Filter

As the name indicates,  the sole purpose of the Shutdown filter is to shut down the PC or to log off after the encoding process is finished. If the line "Shutdown()" is placed in an Avisynth script (this can only be done once and should be done on the last line of the script) and if the encoder requests the last frame of the clip for the first time, then the Shutdown filter initiates a shutdown of the PC. Usually the encoder needs some further time to finish the encoding process, after it has requested the last frame. Hence the Shutdown filter waits for a certain time (the default is 60 seconds) before starting the shutdown. Using the wait option the waiting time can be changed. For example if "Shutdown(wait=600)" is used, then it waits for 600 seconds = 10 minutes until shutdown. Instead of a shutdown one may simply log off by using the method option. With "Shutdown(method=0) the user is simply logged off after the encoding process is finished. method=1 is the default and results in a shutdown, method=2 results in a poweroff, which is usually identical to a shutdown, finally method= 3 reboots the PC after finishing theencoding process. All othervalues for the methodoption are illegal. Oncethe shutdown or the logoff is initiated it can no more be stopped. It can however be stopped during thewaiting time bysimply shutting downthe encoder. For thispurpose the Shutdown filter signals the start of thewaiting periodwith ashortbeep tone.The default frequency of this beep toneis1000Hertz(it can be changed to, say, 2000 Hertz with the option "frequency= 2000") and the default duration is 1000 ms= 1 second (it can bechanged to, say, 500 ms with the option "duration = 500"). Sometimes the user changes his opinion and doesn't want a shutdown anymore though the encoder is processing an AVS script with the Shutdown filter.For thispurpose there is the stamp option. Using the option "stamp= myfile" the filter only initiates a shutdown if "myfile" exists and it checks the existence exactly after the waiting period is over. Thus by erasing or renaming the stamp file one can avoid a shutdown even if the Shutdown filter is in the script. I always use the Shutdown filter with this option. There is one problem with this stamp file method, though. The next day, after the shutdown, the stamp file is still there. But I have only a very limited number of scripts, which are adjusted to a specific encoding process. Now if Iload such an "old" script, it is still triggered for a shutdown, if I accidentally touch the last frame. To avoid such unpleasant shutdown surprises, I have added the< EM> rename< /EM> variable, to rename the stamp file just before the shutdown. The following command is at the end of all my scripts: Shutdown(stamp = "c:\video\stamp", rename= "c:\video\stampn", wait = 300). If now the Shutdown filter triggers a shutdown, it renames the stamp file, such that it can no more cause an accidential shutdown. Clearly the Shutdown filter can only work properly if the encoder doesn't terminate after finishing the encoding process. However, for such encoders the Shutdown filter isn't needed, because in this case the shutdown can be achieved by a batch file using one of the many freeware shutdown utilities. The Shutdown filter tries to terminate all processes gracefully by sending them appropriate messages. However, if a process does not respond properly to theses messages, it is getting killed which may result in data loss. Use this filter only at your own risk and don't blame me.

Known Problems

Alternative

Kurosu has a similar program called Kronos . In fact, Kronos was first.