Beim googeln sehe ich, dass die Verwendung java.io.File#length()
langsam sein kann.
FileChannel
hat eine size()
Methode, die ebenfalls verfügbar ist.
Gibt es in Java eine effiziente Möglichkeit, die Dateigröße zu ermitteln?
Beim googeln sehe ich, dass die Verwendung java.io.File#length()
langsam sein kann.
FileChannel
hat eine size()
Methode, die ebenfalls verfügbar ist.
Gibt es in Java eine effiziente Möglichkeit, die Dateigröße zu ermitteln?
Antworten:
Nun, ich habe versucht, es mit dem folgenden Code zu messen:
Bei Läufen = 1 und Iterationen = 1 ist die URL-Methode meistens am schnellsten, gefolgt vom Kanal. Ich führe dies mit einer Pause etwa 10 Mal frisch. Für einen einmaligen Zugriff ist die Verwendung der URL der schnellste Weg, den ich mir vorstellen kann:
Für Läufe = 5 und Iterationen = 50 zeichnet das Bild anders.
Die Datei muss die Aufrufe an das Dateisystem zwischenspeichern, während Kanäle und URL einen gewissen Overhead haben.
Code:
quelle
stream.available()
gibt die Dateilänge nicht zurück. Es gibt die Anzahl der Bytes zurück, die zum Lesen verfügbar sind, ohne andere Streams zu blockieren. Es ist nicht unbedingt die gleiche Anzahl von Bytes wie die Dateilänge. Um die tatsächliche Länge eines Streams zu erhalten, müssen Sie ihn wirklich lesen (und in der Zwischenzeit die gelesenen Bytes zählen).Der von GHad vorgegebene Benchmark misst neben der Länge viele andere Dinge (wie Reflexion, Instanziierung von Objekten usw.). Wenn wir versuchen, diese Dinge loszuwerden, erhalte ich für einen Anruf die folgenden Zeiten in Mikrosekunden:
Für 100 Läufe und 10000 Iterationen bekomme ich:
Ich habe den folgenden modifizierten Code ausgeführt und als Argument den Namen einer 100-MB-Datei angegeben.
quelle
Alle Testfälle in diesem Beitrag sind fehlerhaft, da sie für jede getestete Methode auf dieselbe Datei zugreifen. Das Festplatten-Caching beginnt also, von dem die Tests 2 und 3 profitieren. Um meinen Standpunkt zu beweisen, habe ich einen von GHAD bereitgestellten Testfall genommen und die Reihenfolge der Aufzählung geändert. Nachfolgend sind die Ergebnisse aufgeführt.
Mit Blick auf das Ergebnis denke ich, dass File.length () wirklich der Gewinner ist.
Die Reihenfolge des Tests ist die Reihenfolge der Ausgabe. Sie können sogar sehen, dass die auf meinem Computer benötigte Zeit zwischen den Ausführungen variiert, aber File.Length (), wenn nicht zuerst, und der erste gewonnene Festplattenzugriff.
quelle
Wenn ich Ihren Code so ändere, dass anstelle einer Ressource eine Datei verwendet wird, auf die über einen absoluten Pfad zugegriffen wird, erhalte ich ein anderes Ergebnis (für 1 Lauf, 1 Iteration und eine 100.000-Byte-Datei - die Zeiten für eine 10-Byte-Datei sind identisch mit 100.000 Byte )
LÄNGE Summe: 33, pro Iteration: 33,0
CHANNEL-Summe: 3626, pro Iteration: 3626,0
URL-Summe: 294, pro Iteration: 294,0
quelle
In Reaktion auf den Benchmark von rgrig muss auch die Zeit berücksichtigt werden, die zum Öffnen / Schließen der FileChannel- und RandomAccessFile-Instanzen benötigt wird, da diese Klassen einen Stream zum Lesen der Datei öffnen.
Nachdem ich den Benchmark geändert hatte, erhielt ich diese Ergebnisse für 1 Iteration in einer 85-MB-Datei:
Für 10000 Iterationen in derselben Datei:
Wenn Sie nur die Dateigröße benötigen, ist file.length () der schnellste Weg, dies zu tun. Wenn Sie die Datei für andere Zwecke wie Lesen / Schreiben verwenden möchten, ist RAF anscheinend die bessere Wahl. Vergiss nur nicht, die Dateiverbindung zu schließen :-)
quelle
Ich bin auf dasselbe Problem gestoßen. Ich musste die Dateigröße und das Änderungsdatum von 90.000 Dateien auf einer Netzwerkfreigabe ermitteln. Wenn Sie Java verwenden und so minimalistisch wie möglich sind, würde dies sehr lange dauern. (Ich musste die URL aus der Datei und auch den Pfad des Objekts abrufen. Sie variierte also etwas, aber mehr als eine Stunde.) Dann verwendete ich eine native ausführbare Win32-Datei und erledigte dieselbe Aufgabe, indem ich nur die Datei ablegte Pfad, geändert und Größe zur Konsole und ausgeführt von Java. Die Geschwindigkeit war unglaublich. Der native Prozess und meine Zeichenfolgenbehandlung zum Lesen der Daten können über 1000 Elemente pro Sekunde verarbeiten.
Obwohl die Leute den obigen Kommentar herabgestuft haben, ist dies eine gültige Lösung und hat mein Problem gelöst. In meinem Fall kannte ich die Ordner, deren Größe ich benötigte, im Voraus und konnte diese in der Befehlszeile an meine win32-App übergeben. Ich ging von Stunden, um ein Verzeichnis zu Minuten zu verarbeiten.
Das Problem schien auch Windows-spezifisch zu sein. OS X hatte nicht das gleiche Problem und konnte so schnell wie das Betriebssystem auf Netzwerkdatei-Informationen zugreifen.
Die Handhabung von Java-Dateien unter Windows ist schrecklich. Der lokale Festplattenzugriff für Dateien ist jedoch in Ordnung. Es waren nur Netzwerkfreigaben, die die schreckliche Leistung verursachten. Windows könnte Informationen über die Netzwerkfreigabe erhalten und die Gesamtgröße in weniger als einer Minute berechnen.
- Ben
quelle
Wenn Sie die Dateigröße mehrerer Dateien in einem Verzeichnis möchten, verwenden Sie
Files.walkFileTree
. Sie können die Größe von der erhaltenBasicFileAttributes
, die Sie erhalten.Dies ist viel schneller, als
.length()
das Ergebnis von aufzurufenFile.listFiles()
oderFiles.size()
das Ergebnis von zu verwendenFiles.newDirectoryStream()
. In meinen Testfällen war es ungefähr 100 mal schneller.quelle
Files.walkFileTree
ist auf Android 26+ verfügbar.Eigentlich denke ich, dass das "ls" schneller sein kann. In Java gibt es definitiv einige Probleme beim Abrufen von Dateiinformationen. Leider gibt es für Windows keine gleichwertige sichere Methode für rekursives ls. (cmd.exes DIR / S kann verwirrt werden und Fehler in Endlosschleifen erzeugen.)
Unter XP, wenn ich auf einen Server im LAN zugreife, brauche ich unter Windows 5 Sekunden, um die Anzahl der Dateien in einem Ordner (33.000) und die Gesamtgröße zu ermitteln.
Wenn ich dies in Java rekursiv durchlaufe, dauert es über 5 Minuten. Ich habe angefangen, die Zeit zu messen, die für file.length (), file.lastModified () und file.toURI () benötigt wird. Dabei habe ich festgestellt, dass 99% meiner Zeit für diese drei Aufrufe benötigt werden. Die 3 Anrufe, die ich eigentlich machen muss ...
Der Unterschied für 1000 Dateien beträgt 15 ms lokal gegenüber 1800 ms auf dem Server. Das Scannen von Serverpfaden in Java ist lächerlich langsam. Wenn das native Betriebssystem denselben Ordner schnell scannen kann, warum kann Java dann nicht?
Als vollständigeren Test habe ich WineMerge unter XP verwendet, um das Änderungsdatum und die Größe der Dateien auf dem Server mit den Dateien vor Ort zu vergleichen. Dies wurde über den gesamten Verzeichnisbaum von 33.000 Dateien in jedem Ordner wiederholt. Gesamtzeit 7 Sekunden. Java: über 5 Minuten.
Die ursprüngliche Aussage und Frage des OP ist also wahr und gültig. Es ist weniger auffällig, wenn es sich um ein lokales Dateisystem handelt. Das lokale Vergleichen des Ordners mit 33.000 Elementen dauert in WinMerge 3 Sekunden und in Java 32 Sekunden lokal. Java versus Native ist also eine 10-fache Verlangsamung in diesen rudimentären Tests.
Java 1.6.0_22 (aktuell), Gigabit LAN und Netzwerkverbindungen, Ping ist weniger als 1 ms (beide im selben Switch)
Java ist langsam.
quelle
Aus dem GHad-Benchmark sind einige Punkte hervorgegangen:
1> Wie von BalusC erwähnt: stream.available () wird in diesem Fall übertragen.
Weil available () eine Schätzung der Anzahl von Bytes zurückgibt , die aus diesem Eingabestream gelesen (oder übersprungen) werden können, ohne beim nächsten Aufruf einer Methode für diesen Eingabestream blockiert zu werden.
Also zuerst die URL dieses Ansatzes entfernen.
2> Wie StuartH erwähnt hat - die Reihenfolge, in der der Testlauf ausgeführt wird, macht auch den Cache-Unterschied aus. Nehmen Sie dies heraus, indem Sie den Test separat ausführen.
Starten Sie nun den Test:
Beim CHANNEL läuft man alleine:
Bei LÄNGE läuft man alleine:
Sieht also so aus, als wäre die LÄNGE hier der Gewinner:
quelle