![]() |
GetFileSize - welches ist die bessere Funktion?
Ich teste gerade ein paar bei der Suchmaschine meiner Wahl gefundenen GetFileSize-Funktionen aus und vergleiche sie.
Mir ist aufgefallen, dass die FindFirst-Funktion (die erste) langsamer ist als die zweite. Habt ihr noch bessere / schnellere? Hier meine Funde
Delphi-Quellcode:
function GetFileSizeA(const aFileName: string): Int64;
var SR: TSearchRec; begin Result := 0; if not FileExists(aFileName) then Exit; if FindFirst(aFileName, faAnyFile and not faDirectory, SR) = 0 then try Result := SR.Size; finally System.SysUtils.FindClose(SR) end; end; function GetFileSizeB(FileName: string): Int64; var Handle: Integer; iFileSize: Int64; begin iFileSize := 0; Handle := FileOpen(FileName, fmOpenRead); if Handle <> -1 then try iFileSize := iFileSize + FileSeek(Handle, Int64(0), 2); finally FileClose(Handle); end; Result := iFileSize; end; function GetFileSizeC(aFileName: string): Int64; var F: TSearchRec; begin if FindFirst(aFileName, faAnyFile, F) = 0 then try Int64Rec(Result).Hi := F.FindData.nFileSizeHigh; Int64Rec(Result).Lo := F.FindData.nFileSizeLow; finally System.SysUtils.FindClose(F); end; end; |
AW: GetFileSize - welches ist die bessere Funktion?
GetFileSizeA macht vorher noch ein überflüssiges FileExists. Ist die immer noch langsamer wenn du das weglässt?
Hast du schon mal GetFileSize aus Winapi.Windows versucht? |
AW: GetFileSize - welches ist die bessere Funktion?
Wenn ich bei A das FileExists weglasse, ist es noch langsamer als B.
Wenn die Datei existiert, ist die Reihenfolge von schnell nach langsam: C, B, A Wenn die Datei nicht existiert: B, C, A (hier ist B etwa doppelt so schnell wie A und B) |
AW: GetFileSize - welches ist die bessere Funktion?
Wie misst du denn die Zeiten? Vielleicht spielt da der Windows-Filecache eine Rolle?
|
AW: GetFileSize - welches ist die bessere Funktion?
Das mit dem FileCache ist nicht so wichtig.
Delphi-Quellcode:
var SW: TStopwatch; i, X: Integer; D1, D2, D3: Integer; const FILE_: string = '\\?\D:\2017-06-12 16-54-48.mkv'; begin SW := TStopwatch.Create; SW.Start; for i := 0 to 9999 do X := GetFileSizeA(FILE_); SW.Stop; D1 := SW.ElapsedMilliseconds; SW := TStopwatch.Create; SW.Start; for i := 0 to 9999 do X := GetFileSizeB(FILE_); SW.Stop; D2 := SW.ElapsedMilliseconds; SW := TStopwatch.Create; SW.Start; for i := 0 to 9999 do X := GetFileSizeC(FILE_); SW.Stop; D3 := SW.ElapsedMilliseconds; Caption := X.ToString; // nur, damit X nicht wegoptimiert wird Caption := D1.ToString + ' - ' + D2.ToString + ' - ' + D3.ToString; |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Wenn der erste aufruf 10 Sekunden dauert und die nachfolgenden 9999 nur 1 Millisekunde, dann sieht es fast langsamer aus, als die andere Variante, wo alle Aufrufe 2 Millisekunden dauern. Im realen Betrieb sieht es oftmals auch so aus, dass es den Cache nicht gibt und alles "neu" eingelesen wird. Außerdem ist dieser Test nicht aussagefähig, denn es ist ein Unterschied, ob man von 10.000 Dateien einmal den Wert ausliest, oder ob man es "sinnlos" 10.000 Mal bei der selben Datei im selben Verzeichnis macht. Und noch sinnloser sind solche "Benchmarks", wenn am Ende noch andere Dinge gemacht werden und es da auf das Zusammenspiel drauf ankommt. Ich kann noch so oft mein Tempo auf 100 Meter messen und das Ergebnis für gut befinden, aber beim Marathon bin ich dennoch voll am Arsch. Und dann kann der eine Code gern 1% schneller sein, aber sein Code ist grauenhaft ... dann doch lieber der andere etwas Langsamere, der aber wenigstens viel schönder und fehlerunanfälliger ist. (außer es komt einmal, was extrem selten vorkommt, doch auf jede Nanosekunde drauf an) Zusätzlich kommt es auch noch auf die Hardware drauf an, denn wenn das Programm beim Entwickler schnell ist, aber in den Zielumgebungen sich selber sinnlos ausbemst, dann bringt es garnichts. Bestes Beispiel sind die schwachsinnigen Dateikopierdialoge vom Windows Explorer ... die könnten und waren früher mal wesentlich schneller, aber jetzt kannst du die Performance komplett vergessen. (beim Kopieren einzelner/weniger Dateien) |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Zitat:
Ist jetzt aber gut. Kannst aufhören auf mich einzuschlagen. |
AW: GetFileSize - welches ist die bessere Funktion?
Wenn es dir egal ist, dass der FileCache mit reinspielt und du so keine vergleichbaren Ergebnisse bekommst, dann ist das ganze doch irgendwie sinnlos. :?
|
AW: GetFileSize - welches ist die bessere Funktion?
Das mit dem Cache ist doch so:
wenn Windows nicht aus dem Cache ließt, dann wird das Ganze sicherlich sehr sehr viel länger dauern. Oder irre ich mich? Das hier dauert bei mir nur rund 180ms. Ich kann mir nicht vorstellen, dass hier ohne Cache gearbeitet wird. |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
|
AW: GetFileSize - welches ist die bessere Funktion?
Ich werfe auch noch eine weitere Funktion in den Raum.
Delphi-Quellcode:
function GetFileSizeD(const FileName: string): Int64;
var FileInfo: TWin32FileAttributeData; begin FillChar(FileInfo, SizeOf(FileInfo), 0); if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, @FileInfo) then begin Int64Rec(Result).Hi := FileInfo.nFileSizeHigh; Int64Rec(Result).Lo := FileInfo.nFileSizeLow; end else Result := 0; // -1 wäre ein besserer Wert, da es Dateien gibt die Größe 0 haben end; |
AW: GetFileSize - welches ist die bessere Funktion?
Deine Funktion ist bisher mit Abstand die schnellste.
Sie ist circa fünfmal so schnell wie B. Ist FindFirst denn wirklich SO langsam und GetFileAttributes(Ex) so viel schneller? Kommt mir schon fast komisch vor. |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Zu FileOpen+FileClose: Virenscanner klinken sich hier sehr gerne ein, und können das ganze gravierend verlangsamen, vor allem beim Öffnen zum Schreiben wird FileClose sehr langsam. Aber auch beim Öffnen zum Lesen prüft der Virenscanner im FileOpen die Datei. |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Vielleicht bekomm,t man damit ja sogar ein DirectoryExists hin. |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
|
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Um Einflüsse durch das Betriebssystem zu minimieren, muss es ausreichend lange laufen. Der Cache wurde als Problem schon genannt. Aber auch jeder Task, Dienst, etc. nimmt Einfluss. Spätestens, wenn irgendein anderer Thread etwas mit der Festplatte macht, werden die Messungen nahezu sinnlos, wenn nur so kurze Zeiträume verwendet werden. Ich nehme außerdem mal an, dass du keinen speziellen Rechner für die Tests verwendest. Das heißt, dass jeder neue Durchlauf andere Bedingungen hat. Wenn du zum Beispiel deine Test-Anwendung neu kompiliert hast, sind die Voraussetzungen verändert. Wenn du also ernsthaft feststellen willst, welches die schnellste Variante ist, musst du erstmal die Testumgebung optimieren. Ansonsten sind deine Ergebnisse nicht wirklich aussagekräftig. btw: Ich habe selbst eine kleine Anwendungen, die Dateien im mittleren 6-stelligen Bereich verarbeitet. Das Einlesen der Informationen dauert i.d.R. weniger als 3 Minuten, wobei die Visualisierung und Sortierung schon enthalten sind. Über eine großartige Optimierung habe ich mir nur wenige Gedanken gemacht, da es meistens ein automatisierter Prozess ist. Daher stellt sich mir die Frage, wofür brauchst du denn eine derartige Optimierung? |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
|
AW: GetFileSize - welches ist die bessere Funktion?
Aus der Praxis heraus würde ich sagen, es ist vom Aufwand her um Längen billiger, die Liste der Systemanforderungen um den Punkt SSD (ggf. sogar PCIe-SSD, M.2 usw.) zu ergänzen, als sich derart in Millisekunden zu versteigen. Allerdings gebe ich zu, es gibt Anwendungsfälle wo das absolut gerechtfertigt ist.
Zitat:
|
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Dann baue deinen Test doch mal so um, dass er statt 180 ms besser ca. 2 Minuten dauert. Das würde zumindest den Cache-Anteil bei der Bewertung stark verringern. Bleibt der Unterschied dann immer noch bei über 50%, würde ich auch von einer Signifikanz ausgehen. |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
GetFileSizeA: 26 Sekunden Laufzeit GetFileSizeD: 2 Sekunden Laufzeit |
AW: GetFileSize - welches ist die bessere Funktion?
Das bestätigt dann ja deine Annahme.
OK, war eigentlich abzusehen, da ja FileExists, FindFirst und FindClose in der D-Variante gar nicht vorkommen. Aber dieser Performance-Test war ja noch einfach. Bei komplizierteren Sachen, die nicht so offensichtlich sind, muss man schon genauer testen. Und darum ging es mir an dieser Stelle. |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Das ist so in etwa das Pendant zum Test-Driven Development, bei dem man einen Test bewusst fehlschlagen lässt, um erstmal den Test-Harness zu überprüfen. |
AW: GetFileSize - welches ist die bessere Funktion?
Alles erledigt was ihr vorgeschlagen habt.
Resultat: A = 2 Minuten, 45 Sekunden D = 31 Sekunden |
AW: GetFileSize - welches ist die bessere Funktion?
Aus gegebenem Anlass habe ich mal gemessen, wie die Funktion GetFileInformationByHandle, mit der auch die Dateigröße bestimmt werden kann, sich schlägt. Hierbei wurde die Zeit bei GetFileAttributesEx und GetFileInformationByHandle für die Verarbeitung von etwa 165.000 Dateien mit zusammen > 3 TB von einer SSD gemessen. Die Messungen erfolgten einerseits nach Rechner-Neustart und dann beim erneuten Einlesen, andererseits mit Defender Antivirus (Windows 10, Echtzeitschutz + Manipulationsschutz) und ohne.
Der Defender Antivirus macht GetFileInformationByHandle ja fast unbrauchbar. Gibt es da ein Gegenmittel? |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
![]() |
AW: GetFileSize - welches ist die bessere Funktion?
[deleted]
|
AW: GetFileSize - welches ist die bessere Funktion?
Schwer zu verstehen. Wie soll das gehen, nicht alle einzeln abzufragen? Ein Test mit dem direkten Aufruf von FindFirstFileExW ergab Zeiten zwischen 13 und 19 Sekunden, also wesentlich langsamer als GetFileAttributesEx. Der Aufruf von GetFileInformationByHandle lässt sich nicht umgehen, da ich die Anzahl der Links und den File Index benötige.
Die Dateigröße benötige ich nur, um festzustellen, ob Dateigleichheit in Frage kommt. Immerhin weiß ich jetzt, dass es etwas bringt, die Dateigrößen zuerst mit GetFileAttributesEx zu ermitteln, um alle diejenigen Dateigrößen auszusondern, die nur einmal vorkommen. |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Es geht ja eben darum, dass du die Anzahl der Abfragen reduzierst. Sprich dass du auf diese Weise gleich alle Dateien in einem Verzeichnis durchgehst. |
AW: GetFileSize - welches ist die bessere Funktion?
Wie soll ich die nicht alle einzeln abfragen? Ich brauche die Daten für jede einzelne Datei. Ich habe eine Klasse
Delphi-Quellcode:
und eine
TDat
Delphi-Quellcode:
, lese die Dateien mittels
TObjectList<TDat>
Delphi-Quellcode:
in ein
TDirectory.GetFiles
Delphi-Quellcode:
ein und lege für jede Datei eine Instanz von TDat an. Bisher habe ich das mit GetFileInformationByHandle gemacht und von allen Dateien die entsprechenden Felder gefüllt; jetzt denke ich, dass ich viel Zeit spare, wenn ich mit GetFileAttributesEx erst einmal die herausfiltere, deren Bytegröße nur einmal vorkommt, und erst dann die verbliebenen mit GetFileInformationByHandle bearbeite. Das wäre nicht nötig, wenn der Defender Antivirus die Aktion nicht so wahnsinnig verlangsamen würde, aber da ist vermutlich nichts zu machen.
TStringDynArray
|
AW: GetFileSize - welches ist die bessere Funktion?
Du könntest das TDirectory.GetFiles overload nehmen, das ein TFilterPredicate akzeptiert. In dem Filter hast du Zugriff auf SearchRec.Size.
|
AW: GetFileSize - welches ist die bessere Funktion?
Wie öffnest du das File-Handle für GetFileInformationByHandle? Man kann bei WinAPI.Windows.CreateFile() statt GENERIC_READ ein FILE_READ_ATTRIBUTES mitgeben. Dann ist CreateFile um einiges schneller. Und der Defender weiß, dass du dich nicht für den Inhalt der Datei interessierst.
Ein Test mit 69669 Dateien liefert bei mir: Mit FILE_READ_ATTRIBUTES (Kalt):
Mit GENERIC_READ (Kalt) (Defender braucht einen ganzen CPU-Kern):
Übrigens die JclNTFS.pas NtfsGetHardLinkInfo Funktion nutzt GENERIC_READ, was sie somit auch extrem langsam macht, wenn ein Virenscanner vorhanden ist. Hier der korrigierte JclNTFS.pas Code:
Delphi-Quellcode:
function NtfsGetHardLinkInfo(const FileName: string; var Info: TNtfsHardLinkInfo): Boolean;
var F: THandle; FileInfo: TByHandleFileInformation; begin Result := False; F := CreateFile(PChar(FileName), {-->}FILE_READ_ATTRIBUTES{<--}, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); if F <> INVALID_HANDLE_VALUE then try ResetMemory(FileInfo, SizeOf(FileInfo)); if GetFileInformationByHandle(F, FileInfo) then begin Info.LinkCount := FileInfo.nNumberOfLinks; Info.FileIndexHigh := FileInfo.nFileIndexHigh; Info.FileIndexLow := FileInfo.nFileIndexLow; Result := True; end; finally CloseHandle(F); end end; |
AW: GetFileSize - welches ist die bessere Funktion?
@Uwe Raabe: Jesus Christus. Das drückt die Zeit auf 2 Sekunden bei Neustart; bei Zweitstart und ausgeschaltetem Defender auf 0,7 Sekunden. Ganz abgesehen davon, dass man die Dateien ja sowieso einlesen muss.
Jetzt erinnere ich mich auch, dass ich damals das SearchRec bei Predicate gesehen hatte, aber das wäre mir im Leben nicht mehr eingefallen. Alle Achtung, Herr Raabe. :thumb: |
AW: GetFileSize - welches ist die bessere Funktion?
Andreas, das ist natürlich auch genial. Und das Beste ist, dass ich es mit Uwes Rat kombinieren kann.
Meine Ergebnisse sind: DateiInfoByHandle
Interessant, dass die Routine nach einem Neustart sogar schneller ist, aber dafür gibt es vermutlich auch eine Erklärung. Erst jetzt fällt mir auf, dass es auch GetFileInformationByHandleEx mit dem Parameter FileInformationClass gibt. Da wäre FileIdInfo interessant, das aber leider nicht die Anzahl der Hardlinks mitliefert, die ich brauche. Anscheinend gibt es aber die Ex-Version nicht bei Delphi. Könnte man nachbauen, aber ist für mich vermutlich unnötig. Jedenfalls bin ich ein großes Stück weitergekommen, hätte ich nicht gedacht. Vielen Dank euch beiden. Offtopic: Ich bin immer noch ein ganz großer Fan von AsyncCalls und habe es nach wie vor in Verwendung. :thumb: |
AW: GetFileSize - welches ist die bessere Funktion?
Es gibt einen FeatureRequest, das SearchRec (oder eine Alternative) auch als Result einer der Suchfunktionen zu bekommen (TArray<Irgendwas>), aber ob sowas Sinnvolles jemals eingebaut wird?
Zitat:
* Wenn man nicht auf den Dateiinhalt zugreifen will, muß der Virenscanner auch nicht den Dateiinhalt prüfen. Bei NTFS sind viele Daten bereits in der MFT hinterlegt, also mit etwas Glück auch gleich für viele Dateien in einem Speicherbereich, so dann nicht für jede Datei einzeln kreuz und quer von sonstwo Daten in kleinen Stückchen geladen werden müssen. Man könnte auch die MFT direkt auslesen, was am Schnellsten ginge, aber dafür sind höhere Rechte nötig (Admin), was dieses Vorhaben etwas unpraktisch gestaltet, davon abgesehn, dass man es wohl alles selbst machen muß, da die passende WinAPI fehlt. Zitat:
|
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
![]() Damit wären wir auch wieder ein bisschen mehr on topic, denn unter den erwähnten NTFS-Tools von Freudenberg findet sich auch NTFSTree, das Dateien mit Größenangabe unter Zugriff auf die MFT auflistet. Herunterzuladen ![]() Zitat:
Delphi-Quellcode:
sowieso, und wenn ich dabei die Dateigröße "umsonst" mitbekomme, ist das prima.
TDirectory.GetFiles
|
AW: GetFileSize - welches ist die bessere Funktion?
Jupp, ich hatte es vor einer Weile auch mal gemacht.
Mit Result:=False es nicht ins Result-Array aufnehmen und den Filter als anonyme Methode, in welcher ich dann mein eigenes Record-Array selbst gefüllt hatte. (brauchte das Änderungsdatum, und in den normalen Results steht ja immer nur der Name) |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
![]() |
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
|
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Delphi-Quellcode:
sogar noch besser:
0
Zitat:
|
AW: GetFileSize - welches ist die bessere Funktion?
Zitat:
Im Code von CreateFileW wird vor dem Aufruf von NtCreateFile der "Access" Parameter mit 0x00100080 ge-OR-t. Das wäre also NtCreateFile(Access or (SYNCHRONIZE or FILE_READ_ATTRIBUTES), ...). Zitat:
![]() 0 hat also die gleiche Bedeutung wie "SYNCHRONIZE or FILE_READ_ATTRIBUTES". Es ist jetzt also nur Geschmackssache ob "CreateFile(FILE_READ_ATTRIBUTES, ...)" oder "CreateFile(0, ...)" eine besser Aussage trifft, was die Intention des Codes ist. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:33 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz