Have some fun with the following "benchmark" test.
I got:
First GetDirectorySize Evil: 9005391285 bytes, (457ms)
First GetDirectorySize Nice: 9005391285 bytes, (487ms)
Old GetDirectorySize Evil: 9005375925 bytes, (457ms)
Old GetDirectorySize Nice: 9005375925 bytes, (463ms)
New GetDirectorySize Evil: 9005375925 bytes, (474ms)
New GetDirectorySize Nice: 9005385465 bytes, (495ms)
Using my C:\ drive which has over 35000 files and almost 4000 folders. (ugh).
Code: Select all
EnableExplicit
Procedure.q FirstDirectorySizeEvil(path$,pattern$ = "*.*")
Protected DirID.l,TotalSize.q
If Right(path$,1)<>"\":path$+"\":EndIf
DirID = ExamineDirectory(#PB_Any,path$,pattern$)
If DirID
While NextDirectoryEntry(DirID)
If DirectoryEntryType(DirID) = #PB_DirectoryEntry_Directory
If DirectoryEntryName(DirID)<>"." And DirectoryEntryName(DirID)<>".."
TotalSize + FirstDirectorySizeEvil(path$+DirectoryEntryName(DirID)+"\",pattern$)
EndIf
EndIf
If DirectoryEntryType(DirID) = #PB_DirectoryEntry_File
TotalSize + DirectoryEntrySize(DirID)
EndIf
;Delay(0)
Wend
FinishDirectory(DirID)
EndIf
ProcedureReturn TotalSize.q
EndProcedure
Procedure.q FirstDirectorySize(path$,pattern$ = "*.*")
Protected DirID.l,TotalSize.q
If Right(path$,1)<>"\":path$+"\":EndIf
DirID = ExamineDirectory(#PB_Any,path$,pattern$)
If DirID
While NextDirectoryEntry(DirID)
If DirectoryEntryType(DirID) = #PB_DirectoryEntry_Directory
If DirectoryEntryName(DirID)<>"." And DirectoryEntryName(DirID)<>".."
TotalSize.q + FirstDirectorySize(path$+DirectoryEntryName(DirID)+"\",pattern$)
EndIf
EndIf
If DirectoryEntryType(DirID) = #PB_DirectoryEntry_File
TotalSize + DirectoryEntrySize(DirID)
EndIf
Delay(0)
Wend
FinishDirectory(DirID)
EndIf
ProcedureReturn TotalSize.q
EndProcedure
Procedure.q GetDirectorySizeEvil(path$, pattern$="")
Protected dir=ExamineDirectory(#PB_Any, path$, pattern$), size.q
If dir
While NextDirectoryEntry(dir)
If DirectoryEntryType(dir) = #PB_DirectoryEntry_File
size+DirectoryEntrySize(dir)
Continue
ElseIf Not DirectoryEntryName(dir) = "." And Not DirectoryEntryName(dir) = ".."
size+GetDirectorySizeEvil(path$+DirectoryEntryName(dir)+"\", pattern$)
Continue
EndIf
;Delay(0)
Wend
FinishDirectory(dir)
EndIf
ProcedureReturn size
EndProcedure
Procedure.q GetDirectorySize(path$, pattern$="")
Protected dir=ExamineDirectory(#PB_Any, path$, pattern$), size.q
If dir
While NextDirectoryEntry(dir)
If DirectoryEntryType(dir) = #PB_DirectoryEntry_File
size+DirectoryEntrySize(dir)
Continue
ElseIf Not DirectoryEntryName(dir) = "." And Not DirectoryEntryName(dir) = ".."
size+GetDirectorySize(path$+DirectoryEntryName(dir)+"\", pattern$)
Continue
EndIf
Delay(0)
Wend
FinishDirectory(dir)
EndIf
ProcedureReturn size
EndProcedure
Procedure.q NewDirectorySizeEvil(path$,pattern$="")
Protected size.q,NewList folder.s(),dir.l,subpath$
size=FileSize(path$)
If size=-2
size=0
If Right(path$,1)<>"\"
path$+"\"
EndIf
AddElement(folder())
folder()=path$
While CountList(folder())>0
subpath$=folder()
dir=ExamineDirectory(#PB_Any,subpath$,pattern$)
If dir
While NextDirectoryEntry(dir)
If DirectoryEntryType(dir)=#PB_DirectoryEntry_File
size+DirectoryEntrySize(dir)
Else
If DirectoryEntryName(dir)<>"." And DirectoryEntryName(dir)<>".."
LastElement(folder())
AddElement(folder())
folder()=subpath$+DirectoryEntryName(dir)+"\"
EndIf
EndIf
;Delay(0)
Wend
FinishDirectory(dir)
EndIf
FirstElement(folder())
DeleteElement(folder(),1)
;Delay(0)
Wend
ClearList(folder())
EndIf
ProcedureReturn size
EndProcedure
Procedure.q NewDirectorySize(path$,pattern$="")
Protected size.q,NewList folder.s(),dir.l,subpath$
size=FileSize(path$)
If size=-2
size=0
If Right(path$,1)<>"\"
path$+"\"
EndIf
AddElement(folder())
folder()=path$
While CountList(folder())>0
subpath$=folder()
dir=ExamineDirectory(#PB_Any,subpath$,pattern$)
If dir
While NextDirectoryEntry(dir)
If DirectoryEntryType(dir)=#PB_DirectoryEntry_File
size+DirectoryEntrySize(dir)
Else
If DirectoryEntryName(dir)<>"." And DirectoryEntryName(dir)<>".."
LastElement(folder())
AddElement(folder())
folder()=subpath$+DirectoryEntryName(dir)+"\"
EndIf
EndIf
Delay(0)
Wend
FinishDirectory(dir)
EndIf
FirstElement(folder())
DeleteElement(folder(),1)
Delay(0)
Wend
ClearList(folder())
EndIf
ProcedureReturn size
EndProcedure
Procedure.l GetTimerResolution(targetres.l=1) ;Default to 1ms target resolution
;TIMECAPS tc;
Protected tc.TIMECAPS,TimerRes.l=#False
If timeGetDevCaps_(tc,SizeOf(TIMECAPS))=#TIMERR_NOERROR
;We must make sure we use a resolution the system is capable of providing.
TimerRes=targetres
If tc\wPeriodMin>TimerRes ;max(TimerRes,tc\wPeriodMin)
TimerRes=tc\wPeriodMin
EndIf
If tc\wPeriodMax<TimerRes ;min(TimerRes,tc\wPeriodMax)
TimerRes=tc\wPeriodMax
EndIf
EndIf
ProcedureReturn TimerRes
EndProcedure ;return 0/#False if failure
OpenConsole()
;I use Multimedia Timers/functions as these can have up to 1ms resolution
;compared to ElapsedMilliseconds() that can have 10ms resolution or worse.
;These multimedia timing features are available on NT3.1 & W95 or later.
Define TimerRes.l
TimerRes=GetTimerResolution(1) ;We want 1ms resolution if available.
If TimerRes=#False
PrintN("Oops! Failed to get timer resolution.")
End
EndIf
timeBeginPeriod_(TimerRes) ;must match timeEndPeriod_()
;Ok, down to business!
Define path$,time1.l,time2.l,size.q
;For a good benchmark choose a big folder with subfolders and many files.
path$="C:\"
;This ensures that disk/system cache is taken advantage of,
;after all we are evaluating the routines not how slow the system is at the first scan.
;This avoid the first run from ending up taking several seconds or worse, ugh...
PrintN("Filling/preloading system disk cache")
size=NewDirectorySizeEvil(path$)
PrintN("Ready?")
PrintN("")
PrintN("3")
Delay(1000) ;to avoid program startup affecting timings too much.
PrintN("2")
Delay(1000) ;it also happens to look cool
PrintN("1")
Delay(1000) ;as well as initialize the debug window.
PrintN("")
;There are Nice and Evil variants of all three.
;the nice variants (the adviced way to do this)
;Uses Delay(0) this forces a context switch,
;all cpu will still be used, but it will not hog the system,
;this is very important in a multitasking os.
;and I really wish more people did this, especially on big/long running
;loops like these. because it is possible for a cpu to get so busy it will
;take minutes for even taskmanager to start, by being nice and using Delay(0)
;the program will use all available cpu that is "free" (not in use),
;this ensures the system remain stable.
;The main difference between the first/old and new methods is that the old uses
;nested procedures/calls, while effective it's not the nices way to do things.
;Although I did not do any stack or memory checking, I do believe my "new" variant
;is more system friendly.
;At the very least it serves as a nice example on building a file index,
;and how to use high precision media timing. *laughs*
;Try this with debugger enabled or disabled, my advice would be with debugger off.
;first run
time1=timeGetTime_()
size=FirstDirectorySizeEvil(path$)
time2=timeGetTime_()
PrintN("First GetDirectorySize Evil: "+StrQ(size)+" bytes, ("+Str(time2-time1)+"ms)")
Delay(1000)
;second run
time1=timeGetTime_()
size=FirstDirectorySize(path$)
time2=timeGetTime_()
PrintN("First GetDirectorySize Nice: "+StrQ(size)+" bytes, ("+Str(time2-time1)+"ms)")
PrintN("")
Delay(1000)
;first run old
time1=timeGetTime_()
size=GetDirectorySizeEvil(path$)
time2=timeGetTime_()
PrintN("Old GetDirectorySize Evil: "+StrQ(size)+" bytes, ("+Str(time2-time1)+"ms)")
Delay(1000)
;second run old
time1=timeGetTime_()
size=GetDirectorySize(path$)
time2=timeGetTime_()
PrintN("Old GetDirectorySize Nice: "+StrQ(size)+" bytes, ("+Str(time2-time1)+"ms)")
PrintN("")
Delay(1000)
;first run new
time1=timeGetTime_()
size=NewDirectorySizeEvil(path$)
time2=timeGetTime_()
PrintN("New GetDirectorySize Evil: "+StrQ(size)+" bytes, ("+Str(time2-time1)+"ms)")
Delay(1000)
;second run new
time1=timeGetTime_()
size=NewDirectorySize(path$)
time2=timeGetTime_()
PrintN("New GetDirectorySize Nice: "+StrQ(size)+" bytes, ("+Str(time2-time1)+"ms)")
PrintN("")
PrintN("Done!")
timeEndPeriod_(TimerRes) ;must match timeBeginPeriod_()
PrintN("")
PrintN("Press enter to quit!")
Input()
CloseConsole()