10
votes

C plus lent que Java: pourquoi?

J'ai vite écrit un programme C extraire la I-Th Line d'un ensemble de fichiers gzipped (contenant environ 500 000 lignes). Voici mon programme C: xxx

comme test, j'ai écrit le code équivalent suivant en Java: xxx

Il arrive que le Java programme était beaucoup plus rapide (~ 1'45 '') pour aller chercher un grand index que le programme C (~ 2'15 '') sur la même machine (j'ai couru ce test plusieurs fois).

Comment Puis-je expliquer cette différence?


10 commentaires

Remarque: les buffsizes ne sont pas égaux d'où les programmes ne font pas la même chose "exacte".


@Sanihuttunen - Le code n'est pas équivalent pour plus de raisons que celles :)


@ Terreception: True, mais c'était ma première observation et semblait suffisamment pour souligner que les programmes ne sont en effet pas égaux.


La mise en œuvre C a une matrice de 10 Mo instanciée sur la pile de processus. Est-ce que cela fonctionne vraiment? La plupart des processus ont des piles plus petites que cela.


Quel compilateur C utilisez-vous et quelles options passez-vous? Assurez-vous que vous activez toutes les optimisations.


J'ai commencé mon programme C avec Bufsiz. J'ai augmenté cette valeur juste pour voir si cela irait plus vite.


@ZR Voir le premier commentaire pour la compilation C.


Peut-être que le compilateur génère un code médiocre exprès en raison du style de codage très peu orthodoxe. C'est ce que j'aurais fait, si j'avais été un compilateur.


@Lundi: N'hésitez pas à modifier mon code pour corriger le style.


Le style de codage @pierre est un peu subjectif et je ne voudrais pas dérailler votre question dans un débat de style de codage. Ces choses sont mieux discutées à Examen de code , où l'on peut demander des commentaires et des améliorations sur le code de travail.


5 Réponses :


15
votes

Parce que fippec () n'est pas très rapide et que vous ajoutez STUF Char-By-Char dans votre fichier de sortie.

appeler fputc_unlocked ou plutôt délimiter les trucs que vous souhaitez ajouter et appeler fwrite () devrait être plus rapide.


2 commentaires

Votre réponse est incorrecte. L'auteur de la question n'a pas spécifié la longueur moyenne d'une ligne dans ses fichiers GZIP.


fputc () est utilisé uniquement pour une seule ligne après avoir sauté un nombre énorme de lignes soi-disant similaires. Pas la Inner Boop Nous devrions rechercher. L'énorme tampon automatique est un meilleur candidat. Rendre la même taille que dans Java (2048) permettrait une comparaison équitable.



12
votes

Eh bien, vos programmes font des choses différentes. Je n'ai pas profilé votre programme, mais de regarder votre code, je soupçonne que cette différence:

Pour construire la ligne, vous l'utilisez en Java: P>

if(curr==index)
{
    fputc(*p,stdout);
}


0 commentaires

0
votes

Je n'ai pas de connaissances plus profondes sur les optimisations du compilateur, mais je suppose que cela fait la différence entre vos programmes. Microbenchmparks comme celui-ci très fort très difficile à obtenir droit et significatif. Voici un article de Brian Goetz qui élabore sur ceci: http: // www.ibm.com/developerworks/java/library/j-JTP02225/index.html


0 commentaires

0
votes

Les très grands tampons peuvent être plus lents. Je vous suggérerais de rendre la taille de la mémoire tampon la même. c'est-à-dire 2 ou 8 kb


2 commentaires

J'ai commencé à utiliser Stdio: Bufsiz: ~ même résultat


Dans C (ZLIB), le grand tampon n'a pas d'importance du tout, en Java, cela fait car il est copié plusieurs fois. Vous pouvez utiliser un fichier mappé en mémoire, tout aussi bien. Java's FileInputStream est (était?) Optimisé pour les plus petits tampons 2K en Win, 8K-Linux, dans ce cas utilise la pile pour allouer, sinon c'est Malloc / gratuit (et certains Malloc sont beaucoup plus lents que la pile), c'est pourquoi le plus petit tampon effectue meilleur. J'ai eu des accidents horribles dans la mémoire natale lorsque vous appelez dans une récursion plus profonde, Double SIGSEG et le processus sont morts (le 2e arrive lorsque vous essayez d'écrire le journal des crash, d'où aucun événement de journal de crash)



22
votes

L'explication la plus probable de la version Java soit plus rapide que la version C est que la version C est incorrecte.

Après avoir fixé la version C, j'ai obtenu les résultats suivants (contrediser votre affirmation que Java est plus rapide que c ): xxx

La tâche consistait à imprimer la ligne 200000-Th de fichier mots.gz . Fichier mots.gz a été généré par gzipping / usr / share / dict / mots .


xxx

10 commentaires

Qu'avez-vous changé dans la version C s'il vous plaît?


Merci ! La première fois que j'ai écrit mon code C, j'ai utilisé des gzgets au lieu de gzread, mais je n'ai pas changé le test dans la boucle sur le tampon.


@Pierre: je vois. Si vous retentissez la référence sur votre ordinateur avec vos fichiers, est plus rapide que Java maintenant?


Oui j'ai fait le test au travail il y a quelques heures. Mais la différence n'était pas aussi grande que prévu.


Pour l'enregistrement, le gzip standard Java est inefficace.


@Bestssss Si l'algorithme GZIP Java's Gzip est traduit de la ligne de ligne sur C, combien de fois fonctionnerait plus rapidement par rapport au code Java d'origine?


@Atom, gonfleur de Java implique. est natif (zlib). Le problème est le problème: le tampon par défaut n'est que de 512 octets, il est copié au moins 2 fois lorsque la lecture du fichier, appelant le code natif w / byte [] implique également 2 exemplaires, il existe également des appels natifs 2 (fichierInputStream et gzipinputtream). Ensuite, CRC32 est également natif - c'est une énorme gaffe (une autre copie d'octets []) et un succès de performances, même en remplacement des bottes CRC32 W / Java Perf. Globalement, le problème est de la manière dont la chaînage des flux d'entrée dans des œuvres Java (tampons intermédiaires partout), que Inflatérial / Deflater n'autorise pas un bytebuffer direct.


@bestsss je ne suis pas sûr de pouvoir croire que votre explication est correcte. Quand je courais "gzip -t quelquefile.gz" sur mon ordinateur portable, je reçois un débit d'environ 15-24 Mo / s. Je suppose que la partie natale de la décompression zippée de Java fonctionne également à cette vitesse. Une copie mémoire, même avec plusieurs tampons intermédiaires, est beaucoup plus rapide que 15-24 Mo / s - de sorte que le chaînage des flux d'entrée ne soit pas le goulot d'étranglement principal. ... mais je n'ai pas analysé ni de référence de la mise en œuvre zip de Java, donc je me trompe.


Pouvez-vous inclure les horaires de -server car il s'agit de la valeur par défaut?


@Atom Le code de GZIP x86 natif est si lent que Azul pourrait le remplacer par une solution Java plus rapide (et pourrait être beaucoup plus facile à fileter). Quoi qu'il en soit, il est clairement pas optimisé. Notez que je n'écrirais pas le code Java de cette manière non plus, semble qu'il y ait des améliorations possibles (bien qu'aucune idée de la façon dont votre code C aime)