Ceci est mon premier message sur Stackoverflow et ma langue maternelle n'est pas l'anglais. S'il vous plaît, excusez-moi pour tout inconvénient que cet article vous apporte. Peut-être que c'est un peu longtemps, alors j'attends votre patience avec impatience. Merci d'avance!
J'ai un extrait de code de langue C. Le travail comporte le nombre de mots dans deux fichiers. J'utilise Pthreads pour résoudre ce problème. Mais je trouve l'ordre de ces deux déclarations strong> p> Nombre_Words (argv [1]); p>
pthread_create (& t1, null, comte_words, (vide *) argv [2]); P>
BlockQuote> affecte les performances du programme, ce qui est opposé à ce que je m'attendais. Voici le code: p> performance: strong> p> i Exécutez le programme à l'aide de "Nom de programme" sur la ligne de commande pour tester le vitesse de course. La sortie est la suivante: p> si la commande comme ceci: p> Nombre_Words (argv [1]); p>
pthread_create (& t1, null, comte_words, (vide *) argv [2]); P>
blockQuote> Le programme fonctionne vite: réel 0.014S p> SI comme ceci: P> pthread_create (& t1, null, comte_words, (vide *) argv [2]); P>
Nombre_Words (argv [1]); p>
blockQuote> programme fonctionne lentement: réel 0.026S p> ce que je m'attendais: strong> p> sur le boîtier 1, le programme exécute le comte_word () d'abord. Après avoir terminé le travail de comptage, il continuera à exécuter pthread_create (). À ce moment-là, le nouveau fil vous aidera à faire le travail de comptage. Donc, le nouveau thread contient le travail après que le fil d'origine complète le travail, qui est en cours d'exécution séquentielle au lieu de courir parallèle. Sur le cas 2, le programme exécute Pthread_Create () d'abord avant tout comptage, de manière à ce qu'il y ait deux threads parallèles faire le comptage. Je m'attends donc à ce que le cas 2 soit plus rapide que le cas 1. Mais je me trompe. Le cas 2 est plus lent. Quelqu'un pourrait-il me donner des informations utiles sur cela? P> Veuillez ignorer que je ne mettez pas de verrouillage mutex sur la variable globale total_wards. Ce n'est pas la partie qui me préoccupe. Et le programme est juste pour les tests. S'il vous plaît pardonnez à ses imperfections. P> ci-dessous est le complément et l'amélioration après avoir lu des suggestions. P> a) Supplément: Le processeur est Intel® Celeron (R) CPU 420 @ 1.60GHz. Un noyau. P> b) Amélioration: j'ai amélioré mon exemple, deux modifications: p> 1) J'ai agrandi les fichiers. File1 est 2080651 octets (environ 2M), File2 est la copie de fichier1. p> 2) J'ai modifié le comte_words (). Lorsque vous atteignez l'extrémité du fichier, utilisez Fsek () pour définir le FP au début et compter à nouveau. Compte à plusieurs reprises les temps comptent. Définir le compte 20. Vous trouverez ci-dessous le code modifié: P> sortie de FAST_VERSION (Nombre_Word () d'abord) et Slow_version (pthread_create () d'abord): P> Administrateur @ ubuntu: ~ $ TIME ./fast_version File1 File2 P> 12241560: Total des mots p> réel 0m5.057s p> utilisateur 0m4.960s p> < p> sys 0m0.048s p> administrateur @ ubuntu: ~ $ TIME ./slow_version File1 File2 P> 12241560: Total des mots p> réel 0m7.636s P > Utilisateur 0m7.280s p> SYS 0M0.048S P> J'ai essayé la commande "Fichier Prignome1 time1 File2" quelques fois. Peut-être qu'il y a une différence sur dixième ou centième d'une seconde à chaque course. Mais les différences ne sont pas beaucoup. P> Cette pièce est ajoutée après avoir fait des expériences selon certaines indications - P> Lorsque vous lancez le deuxième thread après le premier fil terminé son exécution, il n'y a pas de contexte basculant sur le dessus. P>
blockQuote> - par user315052. p> L'expérience est que j'ai amélioré le comte_word (): p> Ajouter une déclaration "Imprimerf ( "de% s \ n", nom de fichier); ", afin que je puisse dire quel fichier (ou fil) fonctionne à cette époque. La sortie de la version rapide est 20 fois "à partir de fichier1", puis 20 fois "à partir de fichier2" et la version lente est "à partir de fichier1" et "à partir de fichier2" imprimé mixte. P> On dirait que la version rapide est plus rapide car il n'y a pas de commutation de contexte. Mais le fait est qu'après la fin de Count_Word (), le fil d'origine n'était pas mort, mais a créé un nouveau fil et a attendu que cela se termine. N'y a-t-il pas de contexte de commutation lorsque le nouveau thread est en cours d'exécution? J'ai regardé l'écran de près et j'ai trouvé que la vitesse d'impression de "à partir de fichier2" est apparemment plus lente que "de File1". Pourquoi? Est-ce parce que la commutation de contexte s'est produite lors du dépôt de fichier2? p> pour la version lente, nous pouvons voir à partir de la sortie la vitesse d'impression de "à partir de fichier1" et "à partir de fichier2" est encore plus lente que la vitesse d'impression de "de File2" dans la version rapide, car son contexte La commutation coûte plus de temps sur le comptage parallèle, tandis que dans la version rapide, le changement de contexte n'est pas si lourd car l'un des threads a terminé son travail et juste en attente. P> donc je pense que la version principale est donc la version rapide Contexte léger et facile basculant contre la version lente. Mais la "vitesse d'impression" vient de mon observation et peut ne pas être aussi stricte. Donc, je ne suis pas sûr de ça. P> p>
3 Réponses :
Comment avez-vous mesuré? em> p>
L'heure réelle n'est pas une indication de la durée de votre programme. Vous devez mesurer l'utilisateur + l'heure du système. De plus, le calendrier significatif au niveau milliseconde dépend beaucoup de la granularité de votre horloge de synchronisation. Si cela fonctionne, disons, à 60Hz, alors vous avez un problème.
À venir avec des repères significatifs est un art En tant que démarrage, vous devriez trouver un moyen d'exécuter vos discussions dans une boucle, disons, 10.000 fois et ajouter des numéros supplémentaires. Ça va au moins vous sortir du problème de synchronisation milliseconde. P>
Suivez vos conseils, j'ai changé mon code, des fichiers agrandis et modifié mon message. Comme je l'ai dit dans le poteau édité, lorsque le comte_words () a atteint la fin du fichier, retournez au début, comptez à nouveau. Cette procédure fonctionne 20 fois. Et j'ai également posté le temps de l'utilisateur et du système. Peut-être que la mesure n'est pas très stricte ni précise. Mais nous pouvons conclure que le Fast est toujours le rapide, non?
Essayez de faire la même mesure mais exécutez votre programme 100 fois et calculez le temps moyen, avec une telle période, l'effet de la mise en cache est loin d'être négligé pour un exemple. P>
Oui. J'ai édité du code. Soit comté_word () faire le travail de comptage à plusieurs reprises sur le même fichier 20 fois. Maintenant, j'ai plus de temps. Mais désolé, je viens de saisir une commande "time proniquement fichier1 fichier2" plusieurs fois et observé le résultat. J'ai trouvé la différence n'est pas beaucoup et suffit pour moi. Donc, je n'ai pas calculé le temps moyen.
Dans un commentaire, vous avez écrit: p>
Le processeur est Intel® Celeron (R) CPU 420 @ 1.60GHz. Un noyau. P> blockQuote>
Comme vous n'avez qu'un seul noyau, vos exécutions de fil sont sérialisées de toute façon. Votre programme avec deux threads exécutés paient simultanément les frais généraux de la commutation contextuelle de fil, car chaque effectue un blocage d'E / S. P>
Lorsque vous lancez le deuxième thread après le premier fil terminé son exécution, il n'y a pas de contexte basculant sur le dessus. P>
Excellente réponse - cela explique presque certainement la confusion de l'OP des timings. +1
Merci pour les informations fournies! Après votre indice, j'ai fait une expérience et j'ai mis à jour mon message. Maintenant, je pense que votre réponse peut ne pas être aussi précise. Il semble y avoir une commutation de contexte après le premier comté_word () (pas le premier thread!) Terminé. Et je pense que la raison est que le coût de la commutation de contexte dans la version lente est plus chère que la version rapide. Mais je ne suis pas sûr. Qu'en penses-tu?
@Wenhaoyang: Qu'est-ce que je voulais dire par "Aucun contexte de commutation de tête" est que chaque thread est autorisé à fonctionner à l'achèvement sans passer en avant et en avant entre eux. Oui, il y aurait deux dans la version rapide. Passer au deuxième fil et retirez-vous après la jointure. Outre le coût du commutateur de contexte lui-même, il existe des coûts associés à un commutateur de contexte, tel que la perturbation du cache de données. La version lente est très certainement le contexte basculant plus de deux fois et chaque fois également le cache de données.
@Wenhaoyang: Pour tester mon explication, vous pouvez créer deux autres versions de votre code. On consiste à déplacer l'appel à comte_words () code> dans
principal () code> pour être après le
pthread_join () code>. Cela devrait toujours être rapide. La seconde est de se débarrasser de votre
pthread ... () code> appels et d'appeler
comte_words () code> deux fois dans
principal () code>. Cela devrait vous donner presque la même heure que votre version rapide.
@ user315052: vous avez raison. Aucun contexte ne pas passer d'avant en arrière. Mais l'expérience que vous avez suggérée ne vous a pas conduit comme prévu. Je ne peux pas non plus le comprendre. J'ai donc amélioré mon expérience en utilisant la fonction de synchronisation pour le rendre plus stricte et envoyer un nouveau POST . Vous pouvez voir mon nouveau post pour ma dernière question. Merci de votre aide!
Sur quel type de machine fonctionnez-vous? Avez-vous réellement deux noyaux?
Et si vous commencez le
total_words ++ code>? Les horaires relatifs restent-ils les mêmes?
Le temps est assez court - comment le mesurez-vous avec précision? Mieux vaut fonctionner avec une contribution beaucoup grande.
Avez-vous activé l'option d'optimisation lors de la compilation? Si vous ne l'avez pas fait, c'est -O2 par défaut pour GCC. J'ai désactivé l'optimisation (-O0) pour les deux exemple et j'avais presque le même timing.
@ user315052 Le processeur est Intel® Celeron (R) CPU 420 @ 1.60GHz. Un noyau.
@NPE Si je commencez le "Total_Words ++", les titrages sont identiques. Fast One est 0,014 et le lent est de 0,026S.
@ROGER_ROWLAND maintenant My File1 est 2080651 octets (environ 2.0m), mon fichier2 est la copie de fichier1. Je gère "Test progname" sur la ligne de commande 10 fois. La durée moyenne du Fast One est d'environ 0,266 et le lent est d'environ 0,387.
@ Bechir i Ran GCC Utilisez le paramètre par défaut. Maintenant, j'ai essayé GCC -O0 et j'ai les deux fois la même chose.
Courir Pthreads sur un seul système de base n'est pas le moyen d'apprendre (ou de tester) le code parallèle. Il cachera la majorité des problèmes et conduira à des hypothèses incorrectes. Tout résultat de synchronisation que vous obtenez sera extrêmement différent d'un système multicœur ou SMP.
@RandyHOWARD Oui, vous avez raison. Mais la bonne partie est que nous pouvons en apprendre quelque chose.
@Wenhaoyang je suis obligé de ne pas être d'accord avec vous là-bas. Vous vous faites vraiment un mauvais service en apprenant une programmation filetée sur un seul système de base. Vous n'apprendrez pas certaines choses qui comptent vraiment sur les conditions de course, le verrouillage, etc. et probablement mal-apprennent d'autres choses parce qu'ils travaillent quand ils ne devraient pas.