Log in

View Full Version : Benchmark loading Image Files


zerowalker
27th April 2014, 17:02
Trying to make a script to somehow benchmark loading image files, pretty much just png with different settings to see the decoding performance.

Avisynth should be able to do this, was thinking of just loading and unloading the same file about 100 times or something, just not sure how to script that, i only know how to add it once and then show it.

Any ideas?

StainlessS
27th April 2014, 19:03
There is a comparison timer for RT versions of eg AverageLuma and builtin's, script included in RT_Stats, together with RT_Timer function,
accurate to about 12-14 millisecs I think.

StainlessS
27th April 2014, 20:58
Here's the script, thought I'de posted on D9 somewhere but could not find it.

TITLE="RT_Stats Timing Test"
RUNS = 5 # Number of runs of each function, slowest and fastest discarded, timed on average of remainder
NFRAMES=2500 # Number of Frames to use from Source Clip

#AVISource("D:\avs\TEST.avi")
Colorbars(pixel_type="YV12")

Trim(0,NFRAMES-1).KillAudio()


# Function names for Display only
Names="
RT_YankChain
YPlaneMin
RT_YPlaneMin
YPlaneMax
RT_YPlaneMax
YPlaneMinMaxDif
RT_YPlaneMinMaxDif
YPlaneMedian
RT_YPlaneMedian
AverageLuma
RT_AverageLuma
Avisynth_ALL
RT_Avisynth_ALL
RT_ALL
"

TESTS=10 # Total number of test incl RT_Yankchain

GSCript("""
Function Test_00(clip c){c for(i=0,framecount-1){current_frame=i RT_YankChain(Last)} return c}
Function Test_01(clip c){c for(i=0,framecount-1){current_frame=i YPlaneMin(Last)} return c}
Function Test_02(clip c){c for(i=0,framecount-1){current_frame=i RT_YPlaneMin(Last)} return c}
Function Test_03(clip c){c for(i=0,framecount-1){current_frame=i YPlaneMax(Last)} return c}
Function Test_04(clip c){c for(i=0,framecount-1){current_frame=i RT_YPlaneMax(Last)} return c}
Function Test_05(clip c){c for(i=0,framecount-1){current_frame=i YPlaneMinMaxDifference(Last)} return c}
Function Test_06(clip c){c for(i=0,framecount-1){current_frame=i RT_YPlaneMinMaxDifference(Last)} return c}
Function Test_07(clip c){c for(i=0,framecount-1){current_frame=i YPlaneMedian(Last)} return c}
Function Test_08(clip c){c for(i=0,framecount-1){current_frame=i RT_YPlaneMedian(Last)} return c}
Function Test_09(clip c){c for(i=0,framecount-1){current_frame=i AverageLuma(Last)} return c}
Function Test_10(clip c){c for(i=0,framecount-1){current_frame=i RT_AverageLuma(Last)} return c}
Function Test_11(clip c){c for(i=0,framecount-1){current_frame=i \
YPlaneMin(Last) YPlaneMax(Last) YPlaneMinMaxDifference(Last) YPlaneMedian(Last) AverageLuma(Last)} return c}
Function Test_12(clip c){c for(i=0,framecount-1){current_frame=i RT_YStats(Last,flgs=$1F)} return c} # As Test_11
Function Test_13(clip c){c for(i=0,framecount-1){current_frame=i RT_YStats(Last,flgs=$7F,lo=0,hi=255)} return c}
# As Test_12 + RT_Stdev+RT_YInRange

Assert(RUNS>=3,"RUNS MUST be at least 3")
Frames=FrameCount()
YankAve=0.0 # Adjustment, will be subtracted from timings of non YankChain fns
PStr=TITLE+CHR(10)+"Timing "+String(TESTS)+"x"+String(RUNS)+"("+String(TESTS*RUNS)+") runs on "+String(NFRAMES)+
\ " Frames (Total Frames="+String(TESTS*RUNS*NFRAMES)+")"
RT_debug(Pstr)
TStart=RT_Timer() # Total Runtime Start
For(Test=0,13) {
EStr="Test_"+RT_NumberString(Test,10,2)+"()" # Func to EVALuate
Times=""
for(Run=1,RUNS) {
s=RT_Timer() Eval(EStr) e=RT_Timer() t = e-s # Time Function
Times=RT_TxtAddStr(Times,String(t))
# RT_Debug(EStr+" "+String(Run)+"] S="+String(s,"%6.2f"),"E="+String(e,"%6.2f"),"Time=",String(t,"%6.2f"))
}
Times=RT_TxtSort(Times,9) # Sort Float strings ascending
Ave = 0.0 #
for(i=1,RUNS-2) {Ave=Ave+Value(RT_TxtGetLine(Times,i))} # excluding slowest and fastest run
Ave=Ave / Float(RUNS-2) # Get Average Time, discarding slowest and fastest run
FPS=Frames / Ave # Ave FPS
Title = RT_StrReplaceMulti(RT_TxtGetLine(Names,Line=Test+1)," "+Chr(10)+Chr(9),Chr(10)+Chr(10)) # Remove any SPACE + TAB
Title = RT_StrPad(Title,18) # Align
OStr=Title+String(ave,"%6.3f")+"Secs :"+String(FPS,"%8.2f")+"FPS"
if(Test==0) {
YankAve=Ave # Average of basic GetFrame() ONLY time
OStr=OStr+" :: Basic GetFrame() [ADJUSTMENT]"
} else {
AdjustedTime = Ave-YankAve
AdjustedFPS = Frames / AdjustedTime
OStr=OStr+" :: ADJUSTED"+String(AdjustedTime,"%6.3f")+"Secs "+String(AdjustedFPS,"%7.2f")+"FPS"
}
RT_Debug(OStr)
PStr=RT_TxtAddStr(PStr,OStr)
}
TEnd=RT_Timer() # Total Runtime End
TTime="Total Runtime = "+String((TEnd-TStart)/60.0,"%6.2f")+" Mins"
RT_Debug(TTime)
PStr=RT_TxtAddStr(PStr,TTime)
# RT_Debug(PStr)
SStr=RT_StrReplace(PStr,Chr(10),"\n") # Convert Chr(10) to '\n' for correct Subtitle display
SubTitle(Sstr,font="Courier New",size=16,lsp=0)
""")


Requires GScript, RT_Stats and maybe Grunt.

EDIT: Here output

00000005 22.05777168 RT_Debug: RT_Stats Timing Test
00000006 22.05779457 RT_Debug: Timing 10x5(50) runs on 2500 Frames (Total Frames=125000)
00000007 22.77612877 RT_Debug: RT_YankChain 0.141Secs :17772.51FPS :: Basic GetFrame() [ADJUSTMENT]
00000008 40.40975571 RT_Debug: YPlaneMin 3.526Secs : 709.02FPS :: ADJUSTED 3.385Secs 738.48FPS
00000009 51.12811279 RT_Debug: RT_YPlaneMin 2.141Secs : 1167.68FPS :: ADJUSTED 2.000Secs 1249.79FPS
00000010 68.74527740 RT_Debug: YPlaneMax 3.521Secs : 710.03FPS :: ADJUSTED 3.380Secs 739.57FPS
00000011 79.45613098 RT_Debug: RT_YPlaneMax 2.141Secs : 1167.86FPS :: ADJUSTED 2.000Secs 1250.00FPS
00000012 98.08608246 RT_Debug: YPlaneMinMaxDif 3.724Secs : 671.32FPS :: ADJUSTED 3.583Secs 697.67FPS
00000013 108.86101532 RT_Debug: RT_YPlaneMinMaxDif 2.151Secs : 1162.07FPS :: ADJUSTED 2.011Secs 1243.37FPS
00000014 126.47580719 RT_Debug: YPlaneMedian 3.516Secs : 711.04FPS :: ADJUSTED 3.375Secs 740.67FPS
00000015 137.16893005 RT_Debug: RT_YPlaneMedian 2.141Secs : 1167.86FPS :: ADJUSTED 2.000Secs 1250.00FPS
00000016 145.49934387 RT_Debug: AverageLuma 1.667Secs : 1500.00FPS :: ADJUSTED 1.526Secs 1638.27FPS
00000017 149.80232239 RT_Debug: RT_AverageLuma 0.859Secs : 2909.23FPS :: ADJUSTED 0.719Secs 3478.66FPS
00000018 229.49786377 RT_Debug: Avisynth_ALL 15.937Secs : 156.86FPS :: ADJUSTED15.797Secs 158.26FPS
00000019 240.43862915 RT_Debug: RT_Avisynth_ALL 2.188Secs : 1142.77FPS :: ADJUSTED 2.047Secs 1221.30FPS
00000020 251.51834106 RT_Debug: RT_ALL 2.214Secs : 1129.35FPS :: ADJUSTED 2.073Secs 1205.98FPS
00000021 251.51916504 RT_Debug: Total Runtime = 3.82 Mins


Was done just now on-line and with AV and HIPS running so the numbers would be in-accurate (lower than normal).

zerowalker
28th April 2014, 02:02
I see, so i could use this, but simple remove all functions and only use "ImageSource(""")" and make it repeatedly load an image, would that make sense or does AVS load it into memory or something making repeated loads meaningless?

StainlessS
28th April 2014, 02:12
Basically yes, could adapt as you wish, is just a template.

EDIT:

Instead of eg AverageLuma, replace with a test to load a single frame, same for other tests which result
in comparison tables for each test.
The tests for eg, Averageluma only test a single frame, all you want to do is load a single frame, much of a muchness really.

zerowalker
28th April 2014, 02:36
Okay i tried to make something up, not sure if it works as expected, a bit confused with all the functions written so just copy pasted and played around:

TITLE="RT_Stats Timing Test"
RUNS = 1000 # Number of runs of each function, slowest and fastest discarded, timed on average of remainder
NFRAMES=1 # Number of Frames to use from Source Clip

ImageReader("image.png")
Trim(0,NFRAMES-1).KillAudio()


# Function names for Display only
Names="
RT_YankChain
YPlaneMin
RT_YPlaneMin
YPlaneMax
RT_YPlaneMax
YPlaneMinMaxDif
RT_YPlaneMinMaxDif
YPlaneMedian
RT_YPlaneMedian
AverageLuma
RT_AverageLuma
Avisynth_ALL
RT_Avisynth_ALL
RT_ALL
"

TESTS=1 # Total number of test incl RT_Yankchain

GSCript("""
Function Test_00(clip c){c for(i=0,framecount-1){current_frame=i RT_YankChain(Last)} return c}
Assert(RUNS>=3,"RUNS MUST be at least 3")
Frames=FrameCount()
YankAve=0.0 # Adjustment, will be subtracted from timings of non YankChain fns
PStr=TITLE+CHR(10)+"Timing "+String(TESTS)+"x"+String(RUNS)+"("+String(TESTS*RUNS)+") runs on "+String(NFRAMES)+
\ " Frames (Total Frames="+String(TESTS*RUNS*NFRAMES)+")"
RT_debug(Pstr)
TStart=RT_Timer() # Total Runtime Start
For(Test=0,0) {
EStr="Test_"+RT_NumberString(Test,10,2)+"()" # Func to EVALuate
Times=""
for(Run=1,RUNS) {
s=RT_Timer() Eval(EStr) e=RT_Timer() t = e-s # Time Function
Times=RT_TxtAddStr(Times,String(t))
# RT_Debug(EStr+" "+String(Run)+"] S="+String(s,"%6.2f"),"E="+String(e,"%6.2f"),"Time=",String(t,"%6.2f"))
}
Times=RT_TxtSort(Times,9) # Sort Float strings ascending
Ave = 0.0 #
for(i=1,RUNS-2) {Ave=Ave+Value(RT_TxtGetLine(Times,i))} # excluding slowest and fastest run
Ave=Ave / Float(RUNS-2) # Get Average Time, discarding slowest and fastest run
FPS=Frames / Ave # Ave FPS
Title = RT_StrReplaceMulti(RT_TxtGetLine(Names,Line=Test+1)," "+Chr(10)+Chr(9),Chr(10)+Chr(10)) # Remove any SPACE + TAB
Title = RT_StrPad(Title,18) # Align
OStr=Title+String(ave,"%6.3f")+"Secs :"+String(FPS,"%8.2f")+"FPS"
if(Test==0) {
YankAve=Ave # Average of basic GetFrame() ONLY time
OStr=OStr+" :: Basic GetFrame() [ADJUSTMENT]"
} else {
AdjustedTime = Ave-YankAve
AdjustedFPS = Frames / AdjustedTime
OStr=OStr+" :: ADJUSTED"+String(AdjustedTime,"%6.3f")+"Secs "+String(AdjustedFPS,"%7.2f")+"FPS"
}
RT_Debug(OStr)
PStr=RT_TxtAddStr(PStr,OStr)
}
TEnd=RT_Timer() # Total Runtime End
TTime="Total Runtime = "+String((TEnd-TStart),"%6.2f")+" Secs"
RT_Debug(TTime)
PStr=RT_TxtAddStr(PStr,TTime)
# RT_Debug(PStr)
SStr=RT_StrReplace(PStr,Chr(10),"\n") # Convert Chr(10) to '\n' for correct Subtitle display
SubTitle(Sstr,font="Courier New",size=16,lsp=0)
""")


One issue is that i can't show Milliseconds, i think i changed from minutes to seconds though, my mathematical abilities are quite a mess so never to sure.

StainlessS
28th April 2014, 10:20
Try this.


TITLE = "ZeroWalker Image Load Benchmark"
PICBASENAME = "PIC\PIC_%06d.BMP" # Might want to load multiple images per ImageReader call, See below IMAGE_END
IMAGE_END = 0 # Last number image to load on each test, avoid duplicate frames, ImageSource(End) defaults to 1000
TESTS = 5 # Total number of tests, ie Names and Functions
RUNS = 5 # Number of runs of each Test_xx Function, slowest and fastest discarded, timed on average of remainder (at least 3)
COUNT = 1000 # Number of times Test_xx Fn calls ImageReader or similar plug

# Function names for Display only
Names=" # LEAVE THIS LINE ALONE, NAMES MUST START ON 2nd LINE
Test_00
Test_01
Test_02
Test_03
Test_04
"

GScript("""
# In each test, Change ImageReader and ImageReader args to whatever. NAMES MUST be in below format ie Test_xx where xx starts at 00.
Function Test_00(String fn,int count,int End){for(i=1,count) {ImageReader(fn,end=End)}}
Function Test_01(String fn,int count,int End){for(i=1,count) {ImageReader(fn,end=End)}}
Function Test_02(String fn,int count,int End){for(i=1,count) {ImageReader(fn,end=End)}}
Function Test_03(String fn,int count,int End){for(i=1,count) {ImageReader(fn,end=End)}}
Function Test_04(String fn,int count,int End){for(i=1,count) {ImageReader(fn,end=End)}}
###
Assert(RUNS>=3,"RUNS MUST be at least 3")
FRAMES = COUNT * (IMAGE_END+1) # Frames per Test_xx function call
PStr=RT_String("%s\n\nTiming %dx%dx%d(%d) runs on %d Frames (Total Frames=%d)",
\ TITLE,TESTS,RUNS,COUNT,TESTS*RUNS*COUNT,IMAGE_END+1,TESTS*RUNS*FRAMES)
RT_Debug(Pstr)
TStart=RT_Timer() # Total Runtime Start
For(Test=0,TESTS - 1) {
EStr=RT_String("Test_%.2d(%c%s%c,%d,%d)",Test,34,PICBASENAME,34,COUNT,IMAGE_END) # Func to EVALuate
Times=""
for(Run=1,RUNS) {
s=RT_Timer() Eval(EStr) e=RT_Timer() t = e-s # Time Function
Times=RT_TxtAddStr(Times,String(t))
# RT_Debug(EStr+" "+String(Run)+"] S="+String(s,"%7.3f"),"E="+String(e,"%7.3f"),"Time=",String(t,"%7.3f"))
}
Times=RT_TxtSort(Times,9) # Sort Float strings ascending
Ave = 0.0 #
for(i=1,RUNS-2) {Ave=Ave+Value(RT_TxtGetLine(Times,i))} # excluding slowest and fastest run
Ave=Ave / Float(RUNS-2) # Get Average Time, discarding slowest and fastest run
FPS = FRAMES / Ave # Ave FPS
Title = RT_StrReplaceMulti(RT_TxtGetLine(Names,Line=Test+1)," "+Chr(10)+Chr(9),Chr(10)+Chr(10)) # Remove any SPACE + TAB
Title = RT_StrPad(Title,18) # Align
OStr=RT_String("%s %7.3fSecs : %9.3f FPS",Title,Ave,FPS)
RT_Debug(OStr)
PStr=RT_TxtAddStr(PStr,OStr)
}
TEnd=RT_Timer() # Total Runtime End
TT=TEnd-TStart
TTime=RT_String("Total Runtime = %7.3f Secs %7.3f Mins",TT,TT/60.0)
RT_Debug(TTime)
PStr=RT_TxtAddStr(PStr,TTime)
# RT_Debug(PStr)
SStr=RT_StrReplace(PStr,Chr(10),"\n") # Convert Chr(10) to '\n' for correct Subtitle display
SubTitle(Sstr,font="Courier New",size=16,lsp=0)
""")

EDITED:

If you want to load multiple images in single ImageReader call then they of course must all be same format.

output

00000005 2.22198462 RT_Debug: ZeroWalker Image Load Benchmark
00000006 2.22200584 RT_Debug:
00000007 2.22202992 RT_Debug: Timing 5x5x1000(25000) runs on 1 Frames (Total Frames=25000)
00000008 8.51536179 RT_Debug: Test_00 1.255Secs : 796.813 FPS
00000009 14.79984951 RT_Debug: Test_01 1.250Secs : 800.000 FPS
00000010 21.08788300 RT_Debug: Test_02 1.255Secs : 796.813 FPS
00000011 27.37723923 RT_Debug: Test_03 1.255Secs : 796.813 FPS
00000012 33.66574860 RT_Debug: Test_04 1.255Secs : 796.813 FPS
00000013 33.66603470 RT_Debug: Total Runtime = 31.437 Secs 0.524 Mins


By the way, there is little point in posting script in code block if all formatting has been removed,
it's like trying to read a page of text with all white space removed, not nice.
Please try to maintain formatting.

PS, To change seconds to milliseconds multiply by 1000.0.

EDIT: Repeatedly loading a single file, disk caching will certainly play a part, but as you just want to compare different
arg timing, should not matter too much.

Gavino
28th April 2014, 11:09
By the way, there is little point in posting script in code block if all formatting has been removed,
it's like trying to read a page of text with all white space removed, not nice.
The problem is that zerowalker used a quote block instead of a code block.

StainlessS
28th April 2014, 13:14
Oops, yes of course.

StainlessS
28th April 2014, 16:54
Here an update, allows you to only call the constructor of eg ImageReader [as previous script, but seems that little is
being done ie a lot faster than calling basic GetFrame(), or sampling a single pixel].
Also allows call basic GetFrame (via RT_YankChain, creates a structure describing the frame, may or may not decompress frame
or some other prep work, but maybe not, I guess it may be source filter dependant). Also allows sample single pixel (via RT_AverageLuma).

This extra functionality is controlled by EXAM config setting, and eg GetFrame()'s or samples every frame loaded, every time if EXAM non zero.


TITLE = "ZeroWalker Image Load Benchmark"
fn = "PIC\PIC_%06d.jpg" # Might want to load multiple images per ImageReader call, See below END
END = 0 # Last number image to load on each test, avoid duplicate frames, ImageSource(End) defaults to 1000
TESTS = 5 # Total number of tests, ie Names and OPS
RUNS = 5 # Number of runs of each OP, slowest and fastest discarded, timed on average of remainder (at least 3)
COUNT = 100 # Number of times OP Fn calls ImageReader or similar plug
EXAM = 0 # 0, No frame test (frame may or may not be decompressed, might be source filter dependant)
# 1, Basic GetFrame ONLY (Reads only structure that describes frame, may or may not be decompressed).
# 2, Test a single pixel (Be sure that frames are decompressed).

# Function names for Display only
Names=" # LEAVE THIS LINE ALONE, NAMES MUST START ON 2nd LINE
Test_00
Test_01
Test_02
Test_03
Test_04
"

# The Operation, ImageReader etc with whatever args, The %s is filled with fn and %d with END.
OPS=""" # LEAVE THIS LINE ALONE, OPS MUST START ON 2nd LINE
ImageReader("%s",end=%d)
ImageReader("%s",end=%d)
ImageReader("%s",end=%d)
ImageReader("%s",end=%d)
ImageReader("%s",end=%d)
"""


GScript("""
Assert(RUNS>=3,"RUNS MUST be at least 3")
FRAMES = COUNT * (END+1) # Frames per Test_xx function call
ETYPE="
NONE
YankChain
RT_AverageLuma
"
ETS=RT_TxtGetLine(ETYPE,EXAM+1)
ETS=RT_StrReplace(RT_StrReplace(ETS,Chr(9),"")," ","")
PStr=RT_String("%s\n\nTiming %dx%dx%d(%d) runs on %d Frames (Total Frames=%d) : EXAM=%d(%s)",
\ TITLE,TESTS,RUNS,COUNT,TESTS*RUNS*COUNT,END+1,TESTS*RUNS*FRAMES,EXAM,ETS)
RT_Debug(Pstr)
FNHEAD=RT_String("Function Test() { GSCript(%c%c%c stim=RT_Timer\n for(i=1,%d) {c=%%s",34,34,34,COUNT)
if(EXAM==0) {FNBODY=""}
Else if(EXAM==1) {FNBODY=RT_String("For(j=0,%d){RT_YankChain(c,n=j)}",END)}
Else {FNBODY=RT_String("For(j=0,%d){RT_AverageLuma(c,n=j,w=1,h=1)}",END)}
FNTAIL=RT_String("}\n return RT_Timer-stim%c%c%c)}\nTest()",34,34,34)
TMPT = FNHEAD+FNBODY+FNTAIL
TStart=RT_Timer() # Total Runtime Start
For(Test=0,TESTS - 1) {
OP=RT_TxtGetLine(OPS,Test+1)
OP=RT_String(OP,fn,END)
EStr=RT_String(TMPT,OP) # Func to EVALuate
# RT_Debug(EStr)
Times=""
for(Run=1,RUNS) {
t = Eval(EStr) # Time the function
Times=RT_TxtAddStr(Times,String(t))
# RT_Debug(EStr+" "+String(Run)+"] S="+String(s,"%7.3f"),"E="+String(e,"%7.3f"),"Time=",String(t,"%7.3f"))
}
Times=RT_TxtSort(Times,9) # Sort Float strings ascending
Ave = 0.0 #
for(i=1,RUNS-2) {Ave=Ave+Value(RT_TxtGetLine(Times,i))} # excluding slowest and fastest run
Ave=Ave / Float(RUNS-2) # Get Average Time, discarding slowest and fastest run
FPS = FRAMES / Ave # Ave FPS
Title = RT_StrReplaceMulti(RT_TxtGetLine(Names,Line=Test+1)," "+Chr(10)+Chr(9),Chr(10)+Chr(10)) # Remove any SPACE + TAB
Title = RT_StrPad(Title,18) # Align
OStr=RT_String("%s %7.3fSecs : %9.3f FPS : %9.3f SPF",Title,Ave,FPS,1.0/FPS)
RT_Debug(OStr)
PStr=RT_TxtAddStr(PStr,OStr)
}
TEnd=RT_Timer() # Total Runtime End
TT=TEnd-TStart
TTime=RT_String("Total Runtime = %7.3f Secs %7.3f Mins",TT,TT/60.0)
RT_Debug(TTime)
PStr=RT_TxtAddStr(PStr,TTime)
# RT_Debug(PStr)
SStr=RT_StrReplace(PStr,Chr(10),"\n") # Convert Chr(10) to '\n' for correct Subtitle display
ColorBars(width=720).SubTitle(Sstr,font="Courier New",size=16,lsp=0)
""")


EDITED: Forgot the %06d in filename


Evals a string like this where EXAM=2, COUNT=10 and END=0


Function Test() { GSCript(""" stim=RT_Timer
for(i=1,10) {c= ImageReader("PIC\PIC_%06d.jpg",end=0)For(j=0,0){RT_AverageLuma(c,n=j,w=1,h=1)}}
return RT_Timer-stim""")}
Test()




PS, you would probably want eg COUNT=100 and END=0 to do 1 frame 100 times per run, OR, COUNT=1 and END=99 to do 100 frames once per run, your choice.

EDIT: What is your intention to use as image sources, ie just one source or multiple eg JpegSource or other.

zerowalker
29th April 2014, 21:47
Seems to work, tested with 2 png files that was compressed differently but identical in pixels.

The light one went for 11 seconds with 10 runs, the other more compressed one went for 21 secs, slower than i expected but probably accurate.

I only want to benchmark png files with different compression to see if it's worth reducing size compared to the speed.

So with these tests it would mean i get about 40% reduced size but 90% reduced speed, not ideal but as it's fast enough already it's at the safe zone.

Thanks:)

StainlessS
30th April 2014, 16:24
OK, if you are happy, I shall do no further work on this.

zerowalker
1st May 2014, 23:21
Yes, it does what i wanted to do, at least i think so.

Simple, Decode X png files, show time taken.

And from i can see it does that, which makes it complete my request, much appreciated:)