CSVReader plus rapide ?

Résolu/Fermé
mikis69 Messages postés 168 Date d'inscription mardi 26 novembre 2013 Statut Membre Dernière intervention 11 février 2019 - Modifié le 5 avril 2017 à 15:35
mikis69 Messages postés 168 Date d'inscription mardi 26 novembre 2013 Statut Membre Dernière intervention 11 février 2019 - 6 avril 2017 à 09:26
Bonjour à tous,

J'ai une petite question concernant les performances d'un petit morceau de code..

Donc en gros, j'ai un fichier csv (avec 190 000 lignes) et pour chaque ligne, je dois télécharger l'archive à l'url présente dans la ligne en question.

Pour cela, j'utilise un objet CSVReader pour lire le fichier et pour chaque ligne, je télécharge et stocke le téléchargement dans un dossier.

Voici le morceau de code :

CSVReader csvReader = new CSVReader(new FileReader(new File("./listPackage.csv")), ';');
File cpanPackages = new File("./cpanPackages/");
cpanPackages.mkdir();
  
csvReader.readAll().parallelStream().forEach(nextLine -> { // 9 secondes
//csvReader.iterator().forEachRemaining(nextLine -> { // 19 secondes
 try {
        if(!new File("./cpanPackages/"+nextLine[2].split("/")[nextLine[2].split("/").length - 1]).exists()) {
                 URL url = new URL(CPAN + nextLine[2]);
       URLConnection conn = url.openConnection();
          InputStream in = conn.getInputStream();
          FileOutputStream out = new FileOutputStream("./cpanPackages/" + nextLine[2].split("/")[nextLine[2].split("/").length - 1]);
          byte[] b = new byte[1024];
          int count;
          while ((count = in.read(b)) >= 0) {
               out.write(b, 0, count);
          }
          out.close();
          in.close();
      }
 } catch (IOException e) {
   e.printStackTrace();
 }
});
  
csvReader.close();


Le soucis avec ce morceau de code c'est que le temps d'exécution est trop élevé.. Pour exemple, j'ai téléchargé les archives (à peu près 35 000 car il y a des doublons dans le fichier csv) en 135 minutes (ce qui est trop long je pense)

Mais ce matin, je me suis rendu compte qu'en modifiant cette ligne

csvReader.iterator().forEachRemaining(nextLine -> {


(500 modules en 19 secondes) en cette ligne

csvReader.readAll().parallelStream().forEach(nextLine -> {


(500 modules en 9 secondes)

EDIT : avec cette ligne de code, j'ai téléchargé les 35 000 modules en 26 minutes...


Qu'en pensez-vous ? Connaissez-vous un moyen d'améliorer ces performances ?

Merci à tous,

Mikis
A voir également:

1 réponse

KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
5 avril 2017 à 21:23
Bonjour,

Tout d'abord il ne faut pas oublier qu'un site sur lequel on télécharge est limité en capacité, donc le mieux que l'on puisse faire c'est atteindre le débit maximum de connexion à ce site.

Pour atteindre cet objectif voici plusieurs axes d'amélioration :

1) Comme tu l'as vu, on peut télécharger plusieurs fichiers en même temps, mais parallelStream utilise un pool de threads dimensionné en fonction du nombre de processeurs de la machine (par exemple 4 chez moi), on peut augmenter le nombre de threads et donc de téléchargements en même temps en modifiant le pool par défaut.

2) Ton code de téléchargement est très rustique, en particulier le byte[1024] pourrait être largement augmenté, même si à mon avis il faut carrément oublier les InputStream/FileOutputStream manuels et laisser faire Java qui a déjà des méthodes utilitaires pour copier des fichiers (via les Channel par exemple)

3) Ça ne te fera pas vraiment gagner du temps, mais quitte à utiliser les Stream autant y aller à fond, il y a donc certaines parties de ton traitement qui peuvent être retravaillées.

Remarques :

a) En testant je constate qu'en gros au delà de 5/6 fichiers en même temps j'atteint la limite de connexion, mais j'ai quand même mis 20 threads pour l'exemple et au final j'ai téléchargé les 35862 fichiers en 10 minutes environ (mais cela peut varier en fonction de ta connexion internet...)

b) Je suis parti directement du fichier complet de ta précédente discussion, je ne me suis donc pas servi du CSVReader qui me paraissait inutile et j'ai mis la gestion des doublons directement dans ce code (avec le
distinct()
)

Voici mon code :

package test;

import java.io.*;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.file.*;
import java.util.List;
import java.util.concurrent.ForkJoinPool;

public class Cpan {

    public static void main(String[] args) throws Exception {
        long time = System.currentTimeMillis();
        List<String> lines = Files.readAllLines(Paths.get("C:/cpan.csv"));
        new ForkJoinPool(20).submit(() -> lines.parallelStream()
                .map(line -> line.split(";")[2]).distinct()
                .forEach(Cpan::download)).get();
        System.out.println(System.currentTimeMillis() - time);
    }

    public static void download(String fileName) {
        File file = new File("C:/cpan/" + fileName);
        file.getParentFile().mkdirs();
        try (FileOutputStream stream = new FileOutputStream(file)) {
            URL url = new URL("http://www.cpan.org/authors/id/" + fileName);
            stream.getChannel().transferFrom(
                   Channels.newChannel(url.openStream()), 0, Long.MAX_VALUE);
        } catch (Exception e) {
            System.err.println(e);
        }
    }
}
0
mikis69 Messages postés 168 Date d'inscription mardi 26 novembre 2013 Statut Membre Dernière intervention 11 février 2019
5 avril 2017 à 23:16
Merci pour ta réponse ^^

Pour ma part, je n'arrive pas à le faire en 10 minutes.. Je l'ai fait tourné deux fois + de 20 min et il était toujours pas fini.. Tu penses que c'est à cause du nombre de thread qui est trop élevé ?

En tout cas, je n'arrive pas au 10 minutes...
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
6 avril 2017 à 09:16
Bonjour,

Je pense que ça vient de ta connexion internet. Il y a quand même 35000 fichiers à télécharger, soit plus de 3 Go de données...

Moi j'ai mis 10 minutes, mais j'étais au maximum de ce que je pouvais télécharger, si tu as une connexion internet avec un débit plus faible et/ou si tu télécharges autre chose en même temps, cela expliquerai que ce soit plus long.

Je ne pense pas que le nombre de threads va changer grand chose dans la mesure où la sollicitation du processeur est quasi nulle et que c'est la carte réseau qui est saturée je doute que ça change vraiment grand chose.

Tu peux essayer sur un plus petit nombre de fichiers (1000 par exemple) et voir combien de temps tu mets avec 5, 10 ou 20 threads pour savoir si cela impacte vraiment la durée de tous les téléchargements.

Remarque : s'il y a vraiment un meilleur nombre de threads, ce sera spécifique à ta configuration (en particulier à ta connexion internet) mais sur un autre PC ça pourrait être un autre nombre de threads qui serait plus rapide.
0
mikis69 Messages postés 168 Date d'inscription mardi 26 novembre 2013 Statut Membre Dernière intervention 11 février 2019
6 avril 2017 à 09:26
Ok !

Merci pour ces informations en tout cas :)
0