Je jouais avec C # et je voulais accélérer un programme. J'ai apporté des changements et j'ai pu le faire. Cependant, j'ai besoin d'aide pour comprendre pourquoi le changement a rendu plus rapide.
J'ai tenté de réduire le code à quelque chose de plus facile à comprendre dans une question. Le score1 et le rapport1 est le moyen plus lent. Le score2 et le rapport2 est le moyen plus rapide. La première méthode stocke d'abord une chaîne et un int dans une structure en parallèle. Ensuite, dans une boucle série, il boucle à travers un tableau de ces structures et écrit leurs données sur un tampon. La deuxième méthode écrit d'abord les données à un tampon à chaîne en parallèle. Ensuite, dans une boucle de série, il écrit les données de chaîne sur un tampon. Voici quelques temps d'exécution des échantillons: P>
Run 1 Time moyen total = 0.492087 Sec Exécuter 2 temps moyen total = 0,273619 sec p>
Lorsque je travaillais avec une version antérieure non parallèle de cela, les temps étaient presque identiques. Pourquoi la différence avec la version parallèle? P>
Même si je réduit la boucle dans le rapport1 Pour écrire une seule ligne de sortie sur le tampon, il est encore plus lent (temps total d'environ 42 secondes). P>
Voici le code simplifié: p>
7 Réponses :
La taille d'une structure doit être généralement inférieure à celle d'un pointeur (si la performance est le problème principal. Microsoft dit que quelque chose de moins de 16 octets fonctionne mieux comme une structure si la sémantique de type référence n'est pas nécessaire), sinon les frais généraux pour la transmettre à des augmentations (parce que Il est adopté par valeur) et serait plus qu'il aurait été pour simplement passer un pointeur. Votre structure contient un pointeur et un int (rendant plus d'un pointeur) pour que vous viviez au-dessus de cela. p>
Voir le quand utiliser Structs em> section de Cet article < / a>. p>
La taille d'une structure doit être généralement inférieure à celle d'un pointeur (Microsoft recommande pas plus de 16 octets) i> - une référence en C # n'est nulle part près de 16 octets. Difficile de faire un type de structure utile inférieur au 32/64 (+ certains frais généraux de ménage) qui nécessite une référence.
@Ed S - C'est pourquoi ce n'est qu'une recommandation et non une application. Vos structures pourraient être plus grandes, mais seules à l'esprit que cela vient avec un coup de performance (que vous devriez peser et prendre en compte lorsque vous décidez d'une structure ou d'une classe comme Microsoft dispose de nombreuses structures de sous-plan).
Ok, mais c'est une mauvaise recommandation. Je veux dire vraiment, montre-moi une struct utile qui est <= la taille d'une référence. Par votre recommandation, chaque type de structure dans le cadre Le cadre lui-même échoue, donc je ne sais pas comment vous pouvez dire que ce serait "typique".
Il n'y a pas de pointeurs dans la structure mais une référence i> à une chaîne et un int
@Ed s Il y a d'autres choses à considérer également que cela peut signifier qu'il est préférable d'utiliser une structure, mais cette question pas b> traite des structures en tant que telles, mais des performances et de cet un des aspects des structures est-ce que b> performance d'impact. La recommandation n'est que sur le côté performance i> pas sur une bonne utilisation des structures, mais cette question concerne la performance i>.
Je ne débattant pas le reste de votre message, je dis simplement que votre recommandation est fausse et ne donne absolument aucun sens.
@Ed S. J'ai édité la réponse pour préciser que ceci n'est que si performance i> est la clé (comme l'OP s'inquiète de cela)
Néanmoins, vous ne pouvez pas simplement dire que «la performance est pire si votre structure est plus grande qu'une référence». Ça dépend de la situation. Par exemple, les structures peuvent vous donner des performances plus déterministes dans l'espace, mais si vous les transmettez beaucoup, vous payez une pénalité pour les copies créées. Vous ne pouvez pas faire bouillir à une déclaration comme "si vos structures sont plus grandes qu'une référence, vous prenez une performance". Ce n'est pas si simple et la première déclaration est toujours fausse.
Juste une idée: je n'ai pas fait de mesure, mais par exemple cette ligne:
var currentWord = words[i]; for (int j = 0; j < currentWord.Length; j++){ char c = currentWord[i]; // ... }
-1, la version foreach code> est optimisée spécifiquement par le compilateur pour ne pas calculer
longueur code> sur et survol. Beaucoup de "je pense" s et pas assez "j'ai testé" faire une mauvaise réponse.
D'accord. Devine sur les optimisations de la compilation ou non des performances sont totalement inutiles.
Je ne sais pas pourquoi les gens descendent tellement ... Si vous effectuez des recherches, vous verrez que le code IL généré par le pourcheach et le pour est différent. ( abi.exdream.com/blog / Post / 2009/04/22 / ... Même Oakcool a affiché un lien intéressant pour cette affaire). En outre, je vous donne quelques idées pour vous aider à enquêter sur la question. Je ne ferai pas le profilage à votre place.
J'ai essayé de l'exécuter à travers un profileur, mais je ne fais pas confiance aux résultats que j'ai eu. (Run1 prend moins de temps que Run2 dedans.) Donc, il n'y a donc aucune réponse concrète là-bas, mais ma suspicion est que le tableau [] valide est le coupable: P>
C'est une allocation de mémoire potentiellement importante que Run1 fait et que Run2 n'est pas. L'attribution de gros morceaux de mémoire peut consommer du temps. p> li>
Il est possible que la matrice se termine loin de toute autre donnée de travail dans la mémoire physique. À tout le moins, il est assez grand de se retrouver dans le grand tas d'objets, alors qu'il semble que la plupart de tout le reste se retrouve sur la pile ou le petit tas d'objets. Cela pourrait signifier que la fonction Score1 doit faire face à plus de cache Misses que la fonction Score2. P> Li> ol>
Ce pourrait être un problème beaucoup plus petit dans le code de série, où vous n'avez que cela se produisant une fois à tout moment. Quand cela se produit pour beaucoup de threads simultanément, cependant, le problème peut être composé de sorte que ce qui vient de provoquer à l'origine une erreur de cache supplémentaire ou deux causant maintenant des défauts de page. P>
J'ai eu quelques problèmes au profil du programme d'origine. Il dirait une routine différente comme goulot d'étranglement. J'apprécie l'idée de défauts de page possibles.
Il y a donc un poste sur CodeProject qui aide à répondre à cela. P>
Eh bien, je viens de naviguer sur votre code et mes premières pensées sont des temps d'action. Dans votre score1, vous effectuez une nouvelle allocation de mémoire pour chaque exécution
valid[i] = new ValidWord
Quel est l'objectif du programme? Le score1 et le score2 ne nous disent rien de ce que l'algorithsme tente de faire. Il a l'air d'essayer d'essayer de trouver un mot qui est trois lettres avec TOUTES-CAPITY 'Us Usul est un mot valide et est ajouté à la liste? P>
Quel est le point d'appeler parallel.foreach sur un tas d'instances de programme lorsque chaque chose est adoptée exactement la même entrée? Et toujours créer un StressBuilder pour chaque mot n'est pas une bonne approche. Vous souhaitez minimiser de nouveaux appels dans des domaines critiques de performance afin de réduire le nombre de fois que la GC doit être lancée. P>
J'ai exécuté votre programme sur le fichier texte: http: //Introcs.cs.princeton .edu / données / mots.txt p>
L'exécution sous le profileur d'échantillonnage VS 2010 montre que le rapport1 est d'environ 78 fois plus lent que Report2 et comptabilise la majeure partie de la différence. Principalement en raison de toutes les appels String.Format et appendez des appels. P>
Le score1 et le score2 sont à peu près la même vitesse avec le score1 allant légèrement plus lent à cause de temps supplémentaire dans StringBuilder.ctor et clr.dll. P>
Mais je soupçonne que votre algorithme pourrait être réécrit sans que tous les constructeurs de cordes ou allocations soient un ordre de grandeur plus rapidement. P>
J'ai supprimé l'entrée différente des routines de score pour simplifier le code posté. +1 pour prendre le temps de le profiler. J'essayais de profiler avant de poster mais j'ai eu une période difficile. Le profileur montrait une place différente comme un goulot d'étranglement.
Tout d'abord, vous parallégez les courses répétées. Cela améliorera votre référence de référence, mais peut ne pas aider votre temps réel de la production. Pour mesurer avec précision combien de temps il faudra pour exécuter en réalité une liste de mots, vous devez avoir exactement une liste de mots à la fois. Sinon, les threads individuels Traitement de toutes les listes sont en concurrence les uns avec les autres dans une certaine mesure des ressources système et le délai par liste souffre, même si le temps de faire toutes les listes au total est plus rapide.
Pour accélérer le temps de Traitez une liste de mots, vous souhaitez traiter les mots individuels de la liste en parallèle, pour une liste exacte à la fois. Pour obtenir suffisamment de définition / taille pour une bonne mesure, faites de la liste très longue ou de traiter la liste de plusieurs reprises en série. P>
Dans votre cas, cela devient un peu délicat, car le StressBuilder avait besoin pour votre produit final. n'est pas documenté comme étant du thread-coffre-fort. Ce n'est pas si mal, cependant. Voici un exemple d'appel d'une liste parallèle pour une liste de mots à une seule liste: p> appeler cela 20 fois dans un traitement séquentiellement normal pour la boucle. Encore une fois, cela pourrait terminer les repères un peu plus lents, mais je pense que cela jouera un monde réel un peu plus vite à cause d'une faille dans votre référence. Notez également que j'ai saisi ce droit dans la fenêtre de réponse - Je n'ai jamais essayé de le compiler, et il n'est donc pas susceptible d'être parfait de la porte. P> Après avoir réalisé votre référence plus précisément. Réfléchissez à la manière dont le code parallèle affectera votre temps de traitement du monde réel, la prochaine étape consiste à faire des hors de curiosité, j'aimerais également savoir comment cette version effectue: p>
Pourquoi essayez-vous de classer un tel code tel que Report1 et Report2? Report1 contient une boucle et un rapport2 ne le fait pas. Peut-être dans la version non parallèle que le compilateur C # déroule la boucle ou une autre magie?
Réduire le rapport1 La boucle d'une itération aide un peu (42 secondes), mais après avoir posté, je pense que c'est la répartition de la matrice dans le score1.
Remarque: la liste des mots est d'environ 14 000 lignes de chaînes. Donc, chaque appel de score1 alloue 14 000 structures.
Je pense que vous devriez essayer de faire du profilage. Il est difficile de dire exactement pourquoi il est plus lent sans mesure correcte. Il est vrai que les allocations sont coûteuses, mais de votre commentaire précédent, je penserais qu'un nouveau [] de struct traduirait: MALLOC (Tailleof (STRIT) * Taille); qui serait un peu rapide. Les structures ne sont pas stockées comme des objets distincts dans les tableaux, mais sont regroupés.
Le score ne sera-t-il toujours pas "3" pour tous ces mots?