54 votes

Pourquoi Java lit-il un gros fichier plus rapidement que C++ ?

J'ai un fichier de 2 Go ( iputfile.txt ) dans lequel chaque ligne du fichier est un mot, comme :

apple
red
beautiful
smell
spark
input

J'ai besoin d'écrire un programme pour lire chaque mot du fichier et imprimer le nombre de mots. Je l'ai écrit en utilisant Java et C++, mais le résultat est surprenant : Java fonctionne 2,3 fois plus vite que C++. Mon code est le suivant :

C++ :

int main() {
    struct timespec ts, te;
    double cost;
    clock_gettime(CLOCK_REALTIME, &ts);

    ifstream fin("inputfile.txt");
    string word;
    int count = 0;
    while(fin >> word) {
        count++;
    }
    cout << count << endl;

    clock_gettime(CLOCK_REALTIME, &te);
    cost = te.tv_sec - ts.tv_sec + (double)(te.tv_nsec-ts.tv_nsec)/NANO;
    printf("Run time: %-15.10f s\n", cost);

    return 0;
}

Sortie :

5e+08
Run time: 69.311 s

Java :

 public static void main(String[] args) throws Exception {

    long startTime = System.currentTimeMillis();

    FileReader reader = new FileReader("inputfile.txt");
    BufferedReader br = new BufferedReader(reader);
    String str = null;
    int count = 0;
    while((str = br.readLine()) != null) {
        count++;
    }
    System.out.println(count);

    long endTime = System.currentTimeMillis();
    System.out.println("Run time : " + (endTime - startTime)/1000 + "s");
}

Sortie :

5.0E8
Run time: 29 s

Pourquoi Java est-il plus rapide que C++ dans cette situation, et comment puis-je améliorer les performances de C++ ?

65voto

laune Points 8921

Vous ne comparez pas la même chose. Le programme Java lit les lignes, en fonction de la nouvelle ligne, tandis que le programme C++ lit les "mots" délimités par des espaces blancs, ce qui représente un travail supplémentaire.

Essayez istream::getline .

Plus tard

Vous pouvez également essayer d'effectuer une opération de lecture élémentaire pour lire un tableau d'octets et rechercher les nouvelles lignes.

Même plus tard

Sur mon vieux portable Linux, jdk1.7.0_21 et don't-tell-me-it's-old 4.3.3 prennent à peu près le même temps, en comparant avec C++ getline. (Nous avons établi que la lecture des mots est plus lente.) Il n'y a pas beaucoup de différence entre -O0 et -O2, ce qui ne me surprend pas, étant donné la simplicité du code dans la boucle.

Dernière note Comme je l'ai suggéré, fin.read(buffer,LEN) avec LEN = 1MB et l'utilisation de memchr pour chercher ' \n Il en résulte une autre amélioration de la vitesse d'environ 20 %, ce qui rend le C (il n'y a plus de C++ à l'heure actuelle) plus rapide que Java.

8voto

James Kanze Points 96599

Il existe un certain nombre de différences significatives dans la façon dont les langues langues traitent I/O qui peuvent tous faire la différence, d'une manière ou d'une autre. d'une manière ou d'une autre.

La première question (et la plus importante) est peut-être la suivante : comment les données sont-elles codées dans le fichier texte ? données sont codées dans le fichier texte. S'il s'agit de caractères à un seul octet ( ISO 8859-1 ou UTF-8 ), Java doit alors le convertir en UTF-16 avant le traitement ; selon la locale, le C++ peut (ou non) également convertir ou effectuer des vérifications supplémentaires.

Comme cela a été souligné (partiellement, du moins), en C++, >> utilise une locale spécifique isspace , getline comparera simplement pour '\n' ce qui est probablement plus rapide. (Les implémentations typiques de isspace utilisera un bitmap, ce qui signifie un accès supplémentaire à la mémoire pour chaque caractère). accès à la mémoire pour chaque caractère).

Les niveaux d'optimisation et les implémentations spécifiques des bibliothèques peuvent également varier. Il n'est pas rare en C++ qu'une implémentation de bibliothèque soit 2 ou 3 fois plus rapide qu'une autre.

Enfin, une différence des plus significatives : C++ fait la distinction entre les fichiers texte et les fichiers binaires. Vous avez ouvert le fichier en mode texte ; cela signifie qu'il sera "prétraité" au plus bas niveau, avant même que les opérateurs d'extraction ne le voient. niveau le plus bas, avant même que les opérateurs d'extraction ne le voient. Ce Cela dépend de la plate-forme : pour les plates-formes Unix, le "prétraitement" n'existe pas. est sans effet ; sur Windows, il convertira les paires CRLF en '\n' , ce qui aura un impact certain sur les performances. Si je me souviens bien Si je me souviens bien (je n'ai pas utilisé Java depuis plusieurs années), Java s'attend à ce que les fonctions de plus haut niveau gèrent cela. que les fonctions de plus haut niveau gèrent cela, donc des fonctions comme readLine sera légèrement plus compliqué. Je ne fais que deviner mais je soupçonne que la logique supplémentaire au niveau supérieur niveau supérieur coûte moins cher en temps d'exécution que le prétraitement du tampon au niveau inférieur. (Si vous faites des tests sous Windows, vous pourriez expérimenter l'ouverture du fichier en mode binaire en C++. Ce Cela ne devrait pas faire de différence dans le comportement du programme lorsque vous utilisez >> ; tout CR supplémentaire sera considéré comme un espace blanc. Avec getline vous devrez ajouter une logique pour supprimer toute queue de poisson. '\r' à votre code).

5voto

Philipp Points 22441

Je pense que la principale différence est que java.io.BufferedReader est plus performant que le std::ifstream car il met en mémoire tampon, alors que l'équipe ifsteam ne le fait pas. Le BufferedReader lit de grandes parties du fichier à l'avance et les transmet à votre programme à partir de la RAM lorsque vous faites appel à readLine() alors que le std::ifstream ne lit que quelques octets à la fois lorsque vous lui demandez de le faire en appelant la fonction >> -opérateur.

L'accès séquentiel à de grandes quantités de données sur le disque dur est généralement beaucoup plus rapide que l'accès à de nombreux petits morceaux un par un.

Une comparaison plus juste serait de comparer std::ifstream au java.io.FileReader sans tampon.

4voto

Alex Suo Points 1384

Je ne suis pas expert en C++, mais vous avez au moins les éléments suivants pour affecter les performances :

  1. Mise en cache au niveau du système d'exploitation pour le fichier
  2. Pour Java, vous utilisez un lecteur tampon et la taille du tampon est par défaut d'une page ou quelque chose comme ça. Je ne suis pas sûr de la façon dont les flux C++ font cela.
  3. Étant donné que le fichier est si volumineux, le JIT est probablement activé et compile le code à barres Java mieux que si vous n'activez aucune optimisation pour votre compilateur C++.

Depuis I/O Le coût est le principal coût ici, je suppose que les raisons 1 et 2 sont les principales.

2voto

rich remer Points 835

Je voudrais aussi essayer d'utiliser mmap au lieu de la lecture/écriture de fichiers standard. Cela devrait permettre à votre système d'exploitation de gérer la lecture et l'écriture, tandis que votre application ne s'occupe que des données.

Il n'y a aucune situation où le C++ ne peut pas être plus rapide que Java, mais cela demande parfois beaucoup de travail de la part de personnes très talentueuses. Mais je ne pense pas que ce soit trop difficile à battre, car il s'agit d'une tâche simple.

mmap pour Windows est décrit dans Cartographie des fichiers ( MSDN ).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X