Schnelles Zählen von Dateien

Für allgemeine Fragen zur Programmierung mit PureBasic.
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Schnelles Zählen von Dateien

Beitrag von Nino »

Die WinAPI-Variante (leicht modifiziert nach dem Code von Rings) ist bei mir mit Dateien auf der lokalen Festplatte meist etwas schneller als die PB-Variante.

Code: Alles auswählen

; PB 4.51

EnableExplicit

DisableDebugger

Procedure.i CountFiles_WinAPI (sPath.s)
   Protected numFiles = 0
   Protected handle
   Protected f.WIN32_FIND_DATA
   
   If Right(sPath, 1) <> "\"
      sPath + "\"
   EndIf
   sPath + "*.*"
   
   ; start a file enum in the specified path
   handle = FindFirstFile_(sPath, f)
   If handle = #INVALID_HANDLE_VALUE
      ProcedureReturn  0
   EndIf 
   
   ; exclude subdirectory from count
   If (f\dwFileAttributes & #FILE_ATTRIBUTE_DIRECTORY) = 0
      numFiles = 1
   Else
      ; it is a directory, recursion here if needed
   EndIf

   While FindNextFile_(handle, f)
      If (f\dwFileAttributes & #FILE_ATTRIBUTE_DIRECTORY) = 0
         numFiles + 1
      Else
         ; it is a directory, recursion here if needed
      EndIf
   Wend
   ; close the file search
   FindClose_(handle)

   ProcedureReturn numFiles
EndProcedure


Procedure.i CountFiles_PB (sPath.s)
   Protected numFiles = 0
   Protected handle
   
   If Right(sPath, 1) <> "\"
      sPath + "\"
   EndIf
   
   ; start a file enum in the specified path
   handle = ExamineDirectory(#PB_Any, sPath, "*.*")
   If handle = 0
      ProcedureReturn  0
   EndIf 
   
   While NextDirectoryEntry(handle)
      If DirectoryEntryType(handle) = #PB_DirectoryEntry_File
         numFiles + 1
      Else
         ; it is a directory, recursion here if needed
      EndIf
   Wend
   ; close the file search
   FinishDirectory(handle)
   
   ProcedureReturn numFiles
EndProcedure


; -- Test
Define path$, count1, count2
Define.q time0, time1, freq, t1, t2

path$ = "C:\windows\system32\"

QueryPerformanceCounter_(@time0)   ; use high-resolution performance counter
count1 = CountFiles_WinAPI(path$) 
QueryPerformanceCounter_(@time1)
QueryPerformanceFrequency_(@freq)
t1 = (time1 - time0) * 1000000 / freq

QueryPerformanceCounter_(@time0)   ; use high-resolution performance counter
count2 = CountFiles_WinAPI(path$) 
QueryPerformanceCounter_(@time1)
QueryPerformanceFrequency_(@freq)
t2 = (time1 - time0) * 1000000 / freq

EnableDebugger

Debug "WinAPI:"
Debug Str(count1) + " Dateien"
Debug Str(t1) + " Mikrosekunden"
Debug ""
Debug "PB:"
Debug Str(count2) + " Dateien"
Debug Str(t2) + " Mikrosekunden"
Die Ergebnisse schwanken immer etwas, aber ich erhalte öfter sowas wie
WinAPI:
2347 Dateien
2834 Mikrosekunden

PB:
2347 Dateien
2455 Mikrosekunden
Grüße, Nino
Benutzeravatar
PureLust
Beiträge: 1145
Registriert: 21.07.2005 00:02
Computerausstattung: Hab aktuell im Grunde nur noch 'nen Lenovo Yoga 2 Pro im Einsatz.
Wohnort: am schönen Niederrhein

Re: Schnelles Zählen von Dateien

Beitrag von PureLust »

Hab' endlich mal ein wenig Zeit gefunden, die Tests bei uns im Netzwerk zu machen.
Und ich muss sagen, ich habe dem guten alten NTFS wirklich Unrecht getan was die Geschwindigkeit angeht. :roll:

@MK_2007: Also wenn bei Dir im Netzwerk das Zählen von 5000 Dateien rund 45 Sekunden in Anspruch nimmt, dann ist da vermutlich irgendwo der Wurm drin (oder greifst Du evtl. über eine WAN-Verbindung auf das Netzlaufwerk zu?).

Hier mal Infos zu den 3 bei uns getesteten Verzeichnissen:

Code: Alles auswählen

 - C:\Temp\FileCountSpeedTest\       - lokale Festplatte (normale 7200 RPM Sata-Platte)
 - Y:\Intranet\FileCountSpeedTest\   - RAID-10 Verbund im Netzwerk (Win2K-Server, Active-Directory)
 - S:\FileCountSpeedTest\            - single SSD im Netzwerk  (Win2K-Server, Active-Directory)
Und die entsprechenden Ergebnisse dazu:
(Wobei ich zu den Ergebnissen der Netzlaufwerke sagen muss, dass momentan auf unseren Servern die nächtliche Datensicherung läuft und die Werte deswegen nicht unbedingt optimal sind.)
Debug Ausgabe hat geschrieben:4610 ms zum Erstellen von 10000 Dateien im Verzeichnis: C:\Temp\FileCountSpeedTest\
17000 ms zum Erstellen von 10000 Dateien im Verzeichnis: Y:\Intranet\FileCountSpeedTest\
17297 ms zum Erstellen von 10000 Dateien im Verzeichnis: S:\FileCountSpeedTest\

Verzeichnisse werden 3 mal eingelesen . . .

Ergebnisse von Verzeichnis : C:\Temp\FileCountSpeedTest\
Durchgang 1 : 31 ms
Durchgang 2 : 16 ms
Durchgang 3 : 31 ms
Durchschnitt : 26 ms

Ergebnisse von Verzeichnis : Y:\Intranet\FileCountSpeedTest\
Durchgang 1 : 172 ms
Durchgang 2 : 203 ms
Durchgang 3 : 172 ms
Durchschnitt : 182 ms

Ergebnisse von Verzeichnis : S:\FileCountSpeedTest\
Durchgang 1 : 172 ms
Durchgang 2 : 156 ms
Durchgang 3 : 172 ms
Durchschnitt : 166 ms

2172 ms zum Löschen aller Dateien im Verzeichnis: C:\Temp\FileCountSpeedTest\
24703 ms zum Löschen aller Dateien im Verzeichnis: Y:\Intranet\FileCountSpeedTest\
24719 ms zum Löschen aller Dateien im Verzeichnis: S:\FileCountSpeedTest\
- Fertig -
Anbei der dazu verwendete Testcode:
(Anzahl der zu testenden Verzeichnisse und Anzahl der Testdurchläufe können leicht an die eigenen Bedürfnisse angepasst werden.)

Code: Alles auswählen

EnableExplicit

#TestDirs     = 3					; Anzahl der Verzeichnisse, die gestestet werden sollen
#DirTestLoops = 3					; Anzahl der Testdurchläufe
#NumOfFiles   = 10000			; Anzahl der anzulegenden Dateien

#FilePrefix   = "TestFile"		; Präfix der anzulegenden Dateien
#FileSuffix   = ".tst"			; Suffix der anzulegenden Dateien

Dim Dir.s(#TestDirs - 1)
Dim Timer(#TestDirs - 1 ,4 + #DirTestLoops * 2)
Define Counter, FileCount, LoopCount, handle, numFiles

; --------------- festlegen, in welchen Verzeichnissen die Testdateien angelegt werden sollen ---------------------

Dir(0) = "C:\Temp\FileCountSpeedTest\"			; lokale Festplatte (normale 7200 RPM Sata-Platte)
Dir(1) = "Y:\Intranet\FileCountSpeedTest\"	; RAID-10 Verbund im Netzwerk
Dir(2) = "S:\FileCountSpeedTest\"				; single SSD im Netzwerk

; --------------- Testverzeichnis und Dateien erstellen --------------------

For Counter = 0 To #TestDirs - 1
	CreateDirectory(Dir(Counter))
	SetCurrentDirectory(Dir(Counter))
	
	Debug "Erstelle " + Str(#NumOfFiles) + " Dateien im Verzeichnis: " + Dir(Counter) + " ..."
	
	DisableDebugger
	
	Timer(Counter,0) = ElapsedMilliseconds()
	
	For FileCount = 1 To #NumOfFiles
		If CreateFile(0, #FilePrefix + Right(Str(100000 + FileCount),5) + #FileSuffix)
			CloseFile(0)
		EndIf
	Next
	
	Timer(Counter,1) = ElapsedMilliseconds()
	
	EnableDebugger
	
	Debug Str(Timer(Counter,1) - Timer(Counter,0)) + " ms zum Erstellen von " + Str(#NumOfFiles) + " Dateien im Verzeichnis: " + Dir(Counter)
	
Next

Debug ""

; --------------- Verzeichnis der erzeugten Dateien mehrfach einlesen --------------------

Debug "Verzeichnisse werden " + Str(#DirTestLoops) + " mal eingelesen . . ."

DisableDebugger

For Counter =  0 To #TestDirs - 1
	Timer(Counter,4) = 0
	
	For LoopCount = 0 To #DirTestLoops-1
		Timer(Counter,5 + LoopCount * 2) = ElapsedMilliseconds()

		numFiles = 0
		handle = ExamineDirectory(#PB_Any, Dir(Counter), "*.*")
		If handle
			While NextDirectoryEntry(handle)
				If DirectoryEntryType(handle) = #PB_DirectoryEntry_File
					numFiles + 1
				EndIf
			Wend
			FinishDirectory(handle)
		EndIf
		
		Timer(Counter,6 + LoopCount * 2) = ElapsedMilliseconds()
		Timer(Counter,4) + Timer(Counter,6 + LoopCount * 2) - Timer(Counter,5 + LoopCount * 2)
		
		If numFiles < #NumOfFiles
			EnableDebugger
			Debug numFiles
			DisableDebugger
		EndIf
		
	Next
Next

EnableDebugger

; --------------- Ergebnis der Durchgänge ausgeben --------------------

Debug ""

For Counter =  0 To #TestDirs - 1
	
	Debug "Ergebnisse von Verzeichnis : " + dir(Counter)
	
	For LoopCount = 0 To #DirTestLoops-1
		Debug "Durchgang " + Str(LoopCount+1) + " : " + Str(Timer(Counter,6 + LoopCount * 2) - Timer(Counter,5 + LoopCount * 2)) + " ms"
	Next
	
	Debug "Durchschnitt : " + Str(Timer(Counter,4) / #DirTestLoops) + " ms"
	Debug ""
Next

; --------------- erzeugte Dateien wieder löschen --------------------

For Counter = 0 To #TestDirs - 1
	
	SetCurrentDirectory(GetHomeDirectory())
	
	DisableDebugger
	
	Timer(Counter,2) = ElapsedMilliseconds()
	DeleteDirectory(Dir(Counter), "*.*")
	Timer(Counter,3) = ElapsedMilliseconds()
	
	EnableDebugger

	Debug Str(Timer(Counter,3) - Timer(Counter,2)) + " ms zum Löschen aller Dateien im Verzeichnis: " + Dir(Counter)
Next

Debug "- Fertig -"
[Dynamic-Dialogs] - komplexe dynamische GUIs einfach erstellen
[DeFlicker] - Fenster flimmerfrei resizen
[WinFX] - Window Effekte (inkl. 'durchklickbares' Window)
Antworten