Frage

Während googeln, sehe ich, dass mit java.io.File#length() kann langsam sein. FileChannel hat eine size() Methode, die zur Verfügung steht als auch.

Gibt es eine effiziente Möglichkeit, in Java um die Dateigröße zu erhalten?

War es hilfreich?

Lösung

Nun habe ich versucht, es zu messen mit dem folgenden Code:

Für Durchläufe = 1 und Iterationen = 1 die URL-Methode ist am schnellsten die meisten Zeit von Kanal gefolgt. Ich betreibe diese mit einer gewissen Pause frische etwa 10-mal. Also für einen Zeitzugriff, über die URL ist der schnellste Weg, ich mich vorstellen kann:

LENGTH sum: 10626, per Iteration: 10626.0

CHANNEL sum: 5535, per Iteration: 5535.0

URL sum: 660, per Iteration: 660.0

Für Durchläufe = 5 und Iterationen = 50 das Bild zieht anders.

LENGTH sum: 39496, per Iteration: 157.984

CHANNEL sum: 74261, per Iteration: 297.044

URL sum: 95534, per Iteration: 382.136

Die Datei muss die Anrufe auf das Dateisystem werden das Caching, während die Kanäle und URL gewissen Overhead haben.

Code:

import java.io.*;
import java.net.*;
import java.util.*;

public enum FileSizeBench {

    LENGTH {
        @Override
        public long getResult() throws Exception {
            File me = new File(FileSizeBench.class.getResource(
                    "FileSizeBench.class").getFile());
            return me.length();
        }
    },
    CHANNEL {
        @Override
        public long getResult() throws Exception {
            FileInputStream fis = null;
            try {
                File me = new File(FileSizeBench.class.getResource(
                        "FileSizeBench.class").getFile());
                fis = new FileInputStream(me);
                return fis.getChannel().size();
            } finally {
                fis.close();
            }
        }
    },
    URL {
        @Override
        public long getResult() throws Exception {
            InputStream stream = null;
            try {
                URL url = FileSizeBench.class
                        .getResource("FileSizeBench.class");
                stream = url.openStream();
                return stream.available();
            } finally {
                stream.close();
            }
        }
    };

    public abstract long getResult() throws Exception;

    public static void main(String[] args) throws Exception {
        int runs = 5;
        int iterations = 50;

        EnumMap<FileSizeBench, Long> durations = new EnumMap<FileSizeBench, Long>(FileSizeBench.class);

        for (int i = 0; i < runs; i++) {
            for (FileSizeBench test : values()) {
                if (!durations.containsKey(test)) {
                    durations.put(test, 0l);
                }
                long duration = testNow(test, iterations);
                durations.put(test, durations.get(test) + duration);
                // System.out.println(test + " took: " + duration + ", per iteration: " + ((double)duration / (double)iterations));
            }
        }

        for (Map.Entry<FileSizeBench, Long> entry : durations.entrySet()) {
            System.out.println();
            System.out.println(entry.getKey() + " sum: " + entry.getValue() + ", per Iteration: " + ((double)entry.getValue() / (double)(runs * iterations)));
        }

    }

    private static long testNow(FileSizeBench test, int iterations)
            throws Exception {
        long result = -1;
        long before = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            if (result == -1) {
                result = test.getResult();
                //System.out.println(result);
            } else if ((result = test.getResult()) != result) {
                 throw new Exception("variance detected!");
             }
        }
        return (System.nanoTime() - before) / 1000;
    }

}

Andere Tipps

Die Benchmark von ghad gegeben misst viele andere Sachen (wie Reflexion, instanziieren Objekte, etc.) neben der Länge zu bekommen. Wenn wir versuchen, dann bekomme ich für einen Anruf dieser Dinge loszuwerden, die folgenden Zeiten in Mikrosekunden:

   file sum___19.0, per Iteration___19.0
    raf sum___16.0, per Iteration___16.0
channel sum__273.0, per Iteration__273.0

Für 100 läuft und 10000 Iterationen erhalte ich:

   file sum__1767629.0, per Iteration__1.7676290000000001
    raf sum___881284.0, per Iteration__0.8812840000000001
channel sum___414286.0, per Iteration__0.414286

Ich habe den folgenden geänderten Code geben als Argument den Namen einer 100MB-Datei ausgeführt werden.

import java.io.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;

public class FileSizeBench {

  private static File file;
  private static FileChannel channel;
  private static RandomAccessFile raf;

  public static void main(String[] args) throws Exception {
    int runs = 1;
    int iterations = 1;

    file = new File(args[0]);
    channel = new FileInputStream(args[0]).getChannel();
    raf = new RandomAccessFile(args[0], "r");

    HashMap<String, Double> times = new HashMap<String, Double>();
    times.put("file", 0.0);
    times.put("channel", 0.0);
    times.put("raf", 0.0);

    long start;
    for (int i = 0; i < runs; ++i) {
      long l = file.length();

      start = System.nanoTime();
      for (int j = 0; j < iterations; ++j)
        if (l != file.length()) throw new Exception();
      times.put("file", times.get("file") + System.nanoTime() - start);

      start = System.nanoTime();
      for (int j = 0; j < iterations; ++j)
        if (l != channel.size()) throw new Exception();
      times.put("channel", times.get("channel") + System.nanoTime() - start);

      start = System.nanoTime();
      for (int j = 0; j < iterations; ++j)
        if (l != raf.length()) throw new Exception();
      times.put("raf", times.get("raf") + System.nanoTime() - start);
    }
    for (Map.Entry<String, Double> entry : times.entrySet()) {
        System.out.println(
            entry.getKey() + " sum: " + 1e-3 * entry.getValue() +
            ", per Iteration: " + (1e-3 * entry.getValue() / runs / iterations));
    }
  }
}

Alle Testfälle in diesem Beitrag sind fehlerhaft, da sie die gleiche Datei für jede Methode getestet zuzugreifen. So Disk-Caching Tritte in denen Prüfungen 2 und 3 profitieren von. Um zu beweisen, meinen Punkt habe ich Testfall durch ghad zur Verfügung gestellt und die Reihenfolge der Aufzählung und unten sind die Ergebnisse.

auf Ergebnis der Suche Ich denke file.length () ist der Gewinner wirklich.

Order of Test ist die Reihenfolge der Ausgabe. Sie können sogar die Zeit sehen auf meiner Maschine zwischen Ausführungen variiert genommen aber file.length (), wenn nicht zuerst und anfallenden ersten Plattenzugriff hat.

---
LENGTH sum: 1163351, per Iteration: 4653.404
CHANNEL sum: 1094598, per Iteration: 4378.392
URL sum: 739691, per Iteration: 2958.764

---
CHANNEL sum: 845804, per Iteration: 3383.216
URL sum: 531334, per Iteration: 2125.336
LENGTH sum: 318413, per Iteration: 1273.652

--- 
URL sum: 137368, per Iteration: 549.472
LENGTH sum: 18677, per Iteration: 74.708
CHANNEL sum: 142125, per Iteration: 568.5

Wenn ich den Code ändern, anstelle einer Ressource eine Datei durch einen absoluten Pfad zugegriffen zu verwenden, erhalte ich ein anderes Ergebnis (für 1 Run, 1 Iteration und eine 100.000 Byte-Datei - mal für eine 10-Byte-Datei ist identisch bis 100.000 Bytes)

LÄNGE Summe: 33, pro Iteration: 33,0

CHANNEL Summe: 3626, pro Iteration: 3.626,0

URL Summe: 294, pro Iteration: 294,0

Als Reaktion auf rgrig der Benchmark, die Zeit, die Filechannel & Random Instanzen benötigt zum Öffnen / Schließen auch berücksichtigt werden, da diese Klassen einen Stream zum Lesen der Datei wird geöffnet.

die Benchmark Nach der Änderung, bekam ich diese Ergebnisse für 1 Iterationen auf einer 85MB-Datei:

file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)

Für 10000 Iterationen auf derselben Datei:

file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)

Wenn alles, was Sie brauchen, ist die Dateigröße, file.length () ist der schnellste Weg, es zu tun. Wenn Sie die Datei für andere Zwecke wie Lesen / Schreiben verwenden möchten, dann scheint RAF eine bessere Wette zu sein. Nur nicht vergessen, die Datei Verbindung zu schließen: -)

import java.io.File;
import java.io.FileInputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;

public class FileSizeBench
{    
    public static void main(String[] args) throws Exception
    {
        int iterations = 1;
        String fileEntry = args[0];

        Map<String, Long> times = new HashMap<String, Long>();
        times.put("file", 0L);
        times.put("channel", 0L);
        times.put("raf", 0L);

        long fileSize;
        long start;
        long end;
        File f1;
        FileChannel channel;
        RandomAccessFile raf;

        for (int i = 0; i < iterations; i++)
        {
            // file.length()
            start = System.nanoTime();
            f1 = new File(fileEntry);
            fileSize = f1.length();
            end = System.nanoTime();
            times.put("file", times.get("file") + end - start);

            // channel.size()
            start = System.nanoTime();
            channel = new FileInputStream(fileEntry).getChannel();
            fileSize = channel.size();
            channel.close();
            end = System.nanoTime();
            times.put("channel", times.get("channel") + end - start);

            // raf.length()
            start = System.nanoTime();
            raf = new RandomAccessFile(fileEntry, "r");
            fileSize = raf.length();
            raf.close();
            end = System.nanoTime();
            times.put("raf", times.get("raf") + end - start);
        }

        for (Map.Entry<String, Long> entry : times.entrySet()) {
            System.out.println(entry.getKey() + " totalTime: " + entry.getValue() + " (" + getTime(entry.getValue()) + ")");
        }
    }

    public static String getTime(Long timeTaken)
    {
        if (timeTaken < 1000) {
            return timeTaken + " ns";
        } else if (timeTaken < (1000*1000)) {
            return timeTaken/1000 + " us"; 
        } else {
            return timeTaken/(1000*1000) + " ms";
        } 
    }
}

Ich lief in das gleiche Problem. Ich brauchte die Dateigröße und Änderungsdatum von 90.000 Dateien auf einer Netzwerkfreigabe zu erhalten. Mit Java und so minimalistisch wie möglich zu sein, wäre es eine sehr lange Zeit in Anspruch nehmen. (Ich brauchte die URL aus der Datei zu erhalten, und den Pfad des Objekts als auch. So ist es etwas verändert, aber mehr als eine Stunde.) Ich habe dann verwendet eine native Win32-Programm, und hat die gleiche Aufgabe, nur die Datei Dumping Pfad, modifiziert und Größe an die Konsole und ausgeführt, dass von Java. Die Geschwindigkeit war erstaunlich. Der native Prozess, und mein String-Handling, die Daten lesen über 1000 Stück pro Sekunde verarbeiten können.

Also auch wenn Menschen Sie den oben Kommentar gewählt, ist dies eine gültige Lösung, und hat mein Problem zu lösen. In meinem Fall wusste, dass ich die Ordner ich die Größen vor der Zeit benötigt, und das konnte ich in der Befehlszeile auf meine win32 app passieren. Ich ging von Stunden ein Verzeichnis Minuten zu verarbeiten.

Das Problem scheint auch Windows-spezifisch. OS X nicht das gleiche Problem haben und könnte Info-Netzwerk-Datei so schnell zugreifen, wie das Betriebssystem so tun könnte.

Java-Datei auf Windows-Handling ist schrecklich. Lokale Festplattenzugriff für Dateien ist jedoch in Ordnung. Es wurde nur Aktien-Netzwerk, das die schreckliche Leistung verursacht. Windows konnte Informationen über die Netzwerkfreigabe erhalten und die Gesamtgröße in weniger als eine Minute berechnen zu.

- Ben

Wenn Sie die Dateigröße von mehreren Dateien in einem Verzeichnis möchten, verwenden Sie Files.walkFileTree . Sie können die Größe von der BasicFileAttributes erhalten, die Sie empfangen.

Dies ist viel schneller als auf dem Ergebnis der .length() Aufruf File.listFiles() oder mit Files.size() auf dem Ergebnis der Files.newDirectoryStream(). In meinem Test Fall war es etwa 100-mal schneller.

Eigentlich, denke ich, dass die „ls“ schneller sein kann. Es gibt auf jeden Fall einige Probleme in Java Umgang mit Datei-Informationen zu bekommen. Leider gibt es keine Entsprechung sichere Methode der rekursiven ls für Windows. (Cmd.exe des DIR / S kann verwirrt und erzeugen Fehler in Endlosschleifen)

Unter Windows XP auf einen Server im LAN zugreifen, es nimmt mir 5 Sekunden in Windows die Anzahl der Dateien in einem Ordner (33.000) zu erhalten, und die Gesamtgröße.

Wenn ich in Java rekursiv durch diese durchlaufen, dauert es mich mehr als 5 Minuten. Ich begann die Zeit gemessen wird file.length (), file.lastModified () zu tun nimmt, und file.toURI () und was ich gefunden habe, ist, dass 99% meiner Zeit von diesen drei Anrufe genommen wird. Die drei Anrufe muss ich eigentlich tun ...

Der Unterschied für 1000 Dateien ist 15ms lokal gegen 1800ms auf dem Server. Das Server-Pfad-Scanning in Java ist unglaublich langsam. Wenn die native OS bei Abtastung schnell sein können, die gleichen Ordner, warum nicht auch Java?

Als ein vollständigen Test, habe ich WineMerge auf XP das Änderungsdatum zu vergleichen, und die Größe der Dateien auf dem Server im Vergleich zu den lokalen Dateien. Dies war in jedem Ordner über den gesamten Verzeichnisbaum von 33.000 Dateien iterieren. Gesamtzeit 7 Sekunden. java:. über 5 Minuten

So ist die ursprüngliche Aussage und Frage aus dem OP wahr ist, und gültig. Sein weniger auffällig, wenn sie mit einem lokalen Dateisystem zu tun. ein lokales Handeln mit 33.000 Artikeln des Ordners vergleichen dauert 3 Sekunden in WinMerge und dauert 32 Sekunden lokal in Java. Also noch einmal, Java im Vergleich zu nativen ist eine 10x Verlangsamung in diesen rudimentären Tests.

Java 1.6.0_22 (neueste), Gigabit LAN und Netzwerkverbindungen, Ping ist weniger als 1 ms (beide im gleichen Schalter)

Java ist langsam.

Von ghad Benchmark, gibt es ein paar Frage Leute erwähnt haben:

1> Wie BalusC erwähnt: stream.available () wird in diesem Fall fließt.

Da verfügbar () gibt eine Schätzung die Anzahl von Bytes, die gelesen werden können (oder übersprungen) von diesem Eingabestrom ohne durch den nächsten Aufruf einer Methode für diesen Eingangsstrom zu blockieren.

So 1. die URL diesen Ansatz zu entfernen.

2> Wie StuartH erwähnt -. Die Reihenfolge der Testlauf auch den Cache Unterschied machen, so dass separat den Test durch laufen nehmen


Jetzt Test starten:

Wenn CHANNEL ein Lauf allein:

CHANNEL sum: 59691, per Iteration: 238.764

Wenn LÄNGE einem Lauf allein:

LENGTH sum: 48268, per Iteration: 193.072

sieht so wie die Länge eins der Gewinner ist hier:

@Override
public long getResult() throws Exception {
    File me = new File(FileSizeBench.class.getResource(
            "FileSizeBench.class").getFile());
    return me.length();
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top