Question

Pendant la recherche sur Google, je constate que l’utilisation de java.io.File # length () peut être lent. FileChannel a un taille () également disponible.

Existe-t-il un moyen efficace en Java d’obtenir la taille du fichier?

Était-ce utile?

La solution

Eh bien, j'ai essayé de le mesurer avec le code ci-dessous:

Pour runs = 1 et itérations = 1, la méthode de l'URL est la plus rapide la plupart du temps, suivie du canal. Je lance ceci avec une pause fraîche environ 10 fois. Donc, pour un accès unique, utiliser l'URL est le moyen le plus rapide auquel je puisse penser:

LENGTH sum: 10626, per Iteration: 10626.0

CHANNEL sum: 5535, per Iteration: 5535.0

URL sum: 660, per Iteration: 660.0

Pour les runs = 5 et les itérations = 50, l’image est différente.

LENGTH sum: 39496, per Iteration: 157.984

CHANNEL sum: 74261, per Iteration: 297.044

URL sum: 95534, per Iteration: 382.136

Le fichier doit mettre en cache les appels au système de fichiers, tandis que les canaux et les URL ont une surcharge.

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;
    }

}

Autres conseils

La référence donnée par GHad mesure beaucoup d’autres choses (telles que la réflexion, l’instanciation d’objets, etc.) en plus de la longueur. Si nous essayons de nous débarrasser de ces choses, alors, pour un appel, les temps suivants sont indiqués en microsecondes:

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

Pour 100 courses et 10000 itérations, je reçois:

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

J'ai exécuté le code modifié suivant en donnant comme argument le nom d'un fichier de 100 Mo.

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));
    }
  }
}

Tous les scénarios de test de ce message sont défectueux car ils accèdent au même fichier pour chaque méthode testée. La mise en cache sur disque est donc bénéfique aux tests 2 et 3. Pour prouver mon argument, j'ai pris le cas test fourni par GHAD et changé l'ordre de dénombrement. Les résultats sont indiqués ci-dessous.

En regardant le résultat, je pense que File.length () est vraiment le gagnant.

L'ordre de test est l'ordre de sortie. Vous pouvez même voir que le temps pris sur ma machine varie entre les exécutions, mais File.Length () lorsqu'il n'est pas premier et que le premier accès au disque est gagné.

---
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

Lorsque je modifie votre code pour utiliser un fichier accédé par un chemin absolu au lieu d'une ressource, j'obtiens un résultat différent (pour 1 exécution, 1 itération et un fichier de 100 000 octets - les temps pour un fichier de 10 octets sont identiques à 100 000 octets)

LONGUEUR somme: 33, par itération: 33,0

CHANNEL somme: 3626, par itération: 3626.0

somme d'URL: 294, par itération: 294.0

En réponse au test de référence de rgrig, le temps nécessaire pour ouvrir / fermer le FileChannel & amp; Les instances de RandomAccessFile doivent également être prises en compte, car ces classes ouvriront un flux pour la lecture du fichier.

Après avoir modifié le repère, j'ai obtenu ces résultats pour 1 itérations sur un fichier de 85 Mo:

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

Pour 10000 itérations sur le même fichier:

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

Si tout ce dont vous avez besoin est la taille du fichier, file.length () est le moyen le plus rapide de le faire. Si vous envisagez d’utiliser le fichier à d’autres fins, comme la lecture / écriture, la RAF semble être un meilleur pari. N'oubliez pas de fermer la connexion de fichier: -)

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";
        } 
    }
}

J'ai rencontré le même problème. J'avais besoin d'obtenir la taille du fichier et la date de modification de 90 000 fichiers sur un partage réseau. En utilisant Java, et aussi minimaliste que possible, cela prendrait beaucoup de temps. (J'avais besoin d'obtenir l'URL du fichier, ainsi que le chemin de l'objet. Ainsi, cela a varié quelque peu, mais plus d'une heure.) J'ai ensuite utilisé un exécutable Win32 natif et j'ai effectué la même tâche, en vidant simplement le fichier. chemin, modifié et la taille à la console, et exécuté à partir de Java. La vitesse était incroyable. Le processus natif et la manipulation de ma chaîne pour lire les données pourraient traiter plus de 1000 éléments par seconde.

Ainsi, même si les utilisateurs ont classé le commentaire ci-dessus au-dessous, cette solution est valide et a résolu mon problème. Dans mon cas, je connaissais les dossiers dont j'avais besoin à l'avance, et je pouvais le transmettre en ligne de commande à mon application win32. Je suis passé d’heures à traiter un répertoire en minutes.

Le problème semblait également être spécifique à Windows. OS X n’avait pas le même problème et pouvait accéder aux informations sur les fichiers du réseau aussi rapidement que le système d’exploitation pouvait le faire.

Java La gestion des fichiers sous Windows est terrible. L'accès au disque local pour les fichiers est correct. Ce sont juste les partages réseau qui ont provoqué cette terrible performance. Windows pourrait également obtenir des informations sur le partage réseau et calculer la taille totale en moins d’une minute.

- Ben

Si vous souhaitez connaître la taille de plusieurs fichiers d'un répertoire, utilisez Files.walkFileTree . Vous pouvez obtenir la taille à partir du BasicFileAttributes que vous recevrez.

Ceci est beaucoup plus rapide que d'appeler .length () sur le résultat de File.listFiles () ou d'utiliser Files.size () . sur le résultat de Files.newDirectoryStream () . Dans mes cas de test, il était environ 100 fois plus rapide.

En fait, je pense que le "ls" peut être plus rapide. Il y a certainement des problèmes en Java concernant l'obtention d'informations sur les fichiers. Malheureusement, il n’existe aucune méthode sûre équivalente de ls récursive pour Windows. (Le répertoire DIR / S de cmd.exe peut être dérouté et générer des erreurs dans des boucles infinies)

Sous XP, pour accéder à un serveur du réseau local, il me faut 5 secondes sous Windows pour obtenir le nombre de fichiers dans un dossier (33 000) et la taille totale.

Lorsque j’ai itéré récursivement dans Java, cela m’a pris plus de 5 minutes. J'ai commencé à mesurer le temps qu'il faut pour exécuter file.length (), file.lastModified () et file.toURI (). Ce que j'ai découvert, c'est que ces 3 appels prennent 99% de mon temps. Les 3 appels que je dois réellement faire ...

La différence pour 1 000 fichiers est de 15 ms en local par rapport à 1 800 ms sur le serveur. L'analyse du chemin du serveur en Java est ridiculement lente. Si le système d'exploitation natif peut analyser rapidement le même dossier, pourquoi Java ne le peut-il pas?

Comme test plus complet, j’ai utilisé WineMerge sur XP pour comparer la date de modification et la taille des fichiers sur le serveur par rapport aux fichiers localement. Cela parcourait l’ensemble de l’arborescence de 33 000 fichiers de chaque dossier. Temps total, 7 secondes. java: plus de 5 minutes.

La déclaration et la question originales du PO sont donc vraies et valides. C'est moins visible lorsqu'il s'agit d'un système de fichiers local. Faire une comparaison locale du dossier avec 33 000 éléments prend 3 secondes dans WinMerge et 32 ??secondes localement en Java. Encore une fois, java versus native est un ralentissement de 10x dans ces tests rudimentaires.

Java 1.6.0_22 (dernière version), réseau LAN gigabit et connexions réseau, le ping est inférieur à 1 ms (les deux dans le même commutateur)

Java est lent.

Du point de vue de GHad, il existe quelques problèmes que les gens ont mentionnés:

1> Comme BalusC mentionné: stream.available () est écoulé dans ce cas.

Parce que disponible () renvoie une estimation du nombre d'octets pouvant être lus (ou ignorés) à partir de ce flux d'entrée sans blocage par le prochain appel d'une méthode pour ce flux d'entrée.

Donc, 1er pour supprimer l'URL de cette approche

2 > Comme StuartH l'a mentionné, l'ordre d'exécution du test fait également la différence entre les caches, supprimez-le donc, exécutez le test séparément.

Maintenant, lancez le test:

Quand CHANNEL est exécuté seul:

CHANNEL sum: 59691, per Iteration: 238.764

Quand LONGUEUR on court seul:

LENGTH sum: 48268, per Iteration: 193.072

Alors, on dirait que celui de longueur est le gagnant ici:

@Override
public long getResult() throws Exception {
    File me = new File(FileSizeBench.class.getResource(
            "FileSizeBench.class").getFile());
    return me.length();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top