Transcription
1. Conseils de performance C#: Laissez-moi vous poser une question. Souhaitez-vous devenir architecte de performance en C forte ? Ok, je dois admettre j'ai inventé ce mot. Mais dans mon livre, un architecte de performance C forte est un développeur senior qui écrit haute performance C
coat pointu . Donc, un développeur senior qui est très conscient des techniques d'optimisation hors couche sur les droits, a appelé à des jeux pour l'analyse de données pour l'acquisition de données en temps réel pour tous ces
environnements cool où la couche rapide est essentielle, Alors aimeriez-vous devenir un architecte de performance C pointu ? Souhaitez-vous rejoindre le club alors ? C' est le cours pour vous. Dans ce cours, je vais vous apprendre une langue hors hacks d'optimisation. Nous allons examiner l'optimisation de base. Donc, c'est le fruit faible suspendu, les réglages faciles qui rendront votre code courir jusqu'à 1000 fois plus rapide. Eh bien, regardez l'optimisation des intermédiaires. Donc ce sont des anneaux réflexe assez avancés de vos codes. Cela vous donnera une petite amélioration de la performance. Nous allons regarder une optimisation vraiment avancée est que nous allons regarder à utiliser pointeurs en C tranchant sur l'écriture directement dans la mémoire dans certains scénarios. C' est plus rapide que d'utiliser les classes .net standard. Nous allons regarder les fondations du point net runtime, donc je vais vous donner un cours de crash dans la pile sur le tas. Comment les types de valeur et les types de référence des magasins sur la pile, sur le tas sur la façon dont les données se déplacent entre la pile sur le tas. Quand on est en cours de course, je t'apprendrai au langage des médias, donc je prendrai la conformité des codes dans un langage intermédiaire. Montrez cette langue intermédiaire,
et je vous expliquerai ce que font toutes les instructions. Donc, à la fin du cours, vous serez en mesure de lire la langue intermédiaire sur Vous prendrez conscience de la façon dont ce compilateur de problème compilé. Stocke manteau dans une langue intermédiaire sur la façon dont je vais instructions peut ralentir votre côte. Donc, c'est un cours assez large. Il contient beaucoup et beaucoup de conférences il ya des quiz pour tester vos connaissances sur. Vous pouvez télécharger le code source que j'ai utilisé pour l'analyse comparative. Souhaitez-vous devenir un architecte de performance C Sharp ? Alors c'est le cours pour vous. J' ai donc créé ce cours pour les développeurs de magasin de niveau moyen ou senior qui veulent
apprendre à écrire rapidement et efficacement C coat pointu pour préparer leur carrière pour des
sujets cool comme les développements de jeu ou l'acquisition de données en temps réel. Merci d'avoir écouté. J' ai hâte de vous accueillir dans ce cours.
2. Introduction à l'optimisation des codes: Parlons donc de l'optimisation des codes. Qu' est-ce que l'optimisation du code ? Eh bien, l'optimisation du
comptage en général implique de modifier le système I t pour le faire fonctionner plus efficacement ou utiliser moins de ressources ou être plus robuste. Par exemple, un programme d'ordinateur peut être optimisé de sorte qu'il s'exécute plus rapidement, qu'il utilise moins de mémoire ou stockage
sur disque ou qu'il soit plus réactif en termes hors de l'interface utilisateur. Nous optimisons le code pour éviter les ralentissements inacceptables. Le code gagnant est en cours d'utilisation. Imaginez que vous cliquiez sur un bouton étiquettes afficher le rapport et que vous ayez à attendre 10 minutes avant
que quelque chose ne s'affiche sur l'interface utilisateur acceptable. Le délai est de deux secondes, nous devons
donc trouver un moyen de générer ce rapport en seulement deux secondes. Nous optimisons également pour rendre nos codes évolutifs. J' ai personnellement expérimenté des sites Web qui fonctionnent bien avec 10 utilisateurs simultanés, mais complètement en panne lorsque 50 utilisateurs se sont rendus sur le site tout en même temps. Il y a même un nom pour ça. Il s'appelle les noms d'effet slash fille lorsque les sites Web populaires slash DOT présente un site Web sur sa page d'accueil, qui se bloque rapidement parce que des millions de visiteurs cliquent sur le lien hypertexte en cas de surcharge. Le serveur Web, une chanson sa connaissance hors de l'optimisation du code va vous aider à remorquer éviter ces
catastrophes potentielles . Donc, dans ce cours, nous allons regarder. C' est ainsi que l'exécution d'un programme plus rapide. Nous avons un certain nombre de stratégies à notre disposition. Pour atteindre cet objectif, nous pouvons réécrire des algorithmes pour obtenir le même résultat. Avec moins de froid, nous pouvons éviter des instructions inutiles. Nous pouvons nous assurer que les bibliothèques que nous utilisons sont spécialement conçues pour la tâche à accomplir. Nous pouvons réduire la consommation de mémoire autant que possible, et nous pouvons éviter les scénarios où le code bloque, attendant une ressource lente. L' optimisation des performances de ce cours se situe dans une ou plusieurs de ces cinq catégories. Mais avant de commencer, un dernier mot d'avertissement. Il y a des citations célèbres de Michael Jackson sur l'informaticien britannique Michael Jackson, pas l'autre gars. La citation va comme ça. La première règle de l'optimisation du programme est. Ne le fais pas. La deuxième salle d'optimisation du programme, qui est réservée aux experts, est de ne pas le faire encore. Le célèbre informaticien Donald Neuf avait des vêtements similaires. L' optimisation prématurée est la racine de tout mal. La pensée derrière ces citations est que votre code ira complètement pour aider. Si vous essayez de vous concentrer sur l'optimisation des performances dès le début pendant que vous
travaillez encore sur votre programme, votre manteau est fluide et évolue dans des directions inattendues. Il est impossible de prédire à l'avance de manière fiable où seront les goulets d'étranglement des performances. Voici un exemple près de la fin de ce cours, vous apprendrez que les pointeurs peuvent accélérer votre manteau, mais seulement si vous les utilisez d'une manière très spécifique. Alors imaginez le facteur re dans l'ensemble de votre programme pour utiliser des pointeurs, puis, quelques semaines plus tard, vous devez réfléchir sur votre algorithme et vers le bas. Le point d'optimisation ne fonctionne plus. Donc maintenant vous êtes coincé avec un manteau dangereux, difficile à lire qui est en fait plus lent que le manteau bien écrit, propre et sûr. Cela n'utilise pas de pointeurs, donc vous allez probablement devoir tout annuler. Un autre exemple. Vous avez passé des semaines à presser chaque once possible de performances d'une fonction de bibliothèque, et finalement vous avez réussi à optimiser la fonction pour qu'elle s'exécute en moins d'une millisecondes. Félicitations,
Ensuite, votre programme évolue à nouveau, et au moment où vous allez dans la production, votre message est appelé 99% du temps, juste après un noyau de base de données qui prend 300 millisecondes pour terminer. Donc, toute votre optimisation zwart de Nothing. Donc, le plan d'action recommandé est d'écrire du code modulaire simple et
clair sur Quitter l'organisation jusqu'à la toute fin lorsque vous savez de manière fiable où les goulets d'étranglement de performance vont être. Cependant, il existe des exceptions à cette règle. Je vais vous laisser avec la version complète de la citation de Donald. Nous devrions oublier les petites économies, disons environ 97 % du temps. L' optimisation prématurée est la racine du mal. Pourtant, nous ne devrions pas laisser tomber les opportunités dans ce 3% critique. Donc, si vous pouvez voir clairement les 3% dès le début, n'hésitez pas à commencer à optimiser tout de suite.
3. Qu'est ce que la mémoire de l'empile ?: était simplement la pile est un bloc de mémoire qui est utilisé pour appeler des méthodes et stocker des variables locales. Laisse-moi dessiner ça ici sur le tableau noir. Je vais dessiner la pile comme une colonne verticale comme celle-ci. Non, quand je commence à exécuter du code, cette pile sera initialement vide. Mais quand Mike Oates appelle les méthodes cela se produit, les paramètres de la méthode l'adresse de retour sur toutes les variables locales hors des méthodes sont mis sur la pile. Ce bloc entier de données est appelé bloc de pile. Donc, qu'arrive-t-il à la pile lorsque mes codes appellent une autre méthode de l'intérieur de cette méthode, ce
qui revient la même chose ? Les paramètres de la méthode retournent l'adresse sur les variables locales de la nouvelle méthode que j'ai mise sur la pile juste au-dessus du cadre de la pile précédente. C' est pourquoi ça s'appelle une pile. Les informations sont empilées les unes sur les autres. Que se passe-t-il lorsque mon culte rencontre une déclaration de retour ? Comme vous le savez probablement, une instruction return met fin à une méthode et revient au code appelant. Maintenant sur la pile. C' est ce qui se passe. Le cadre de pile entier qui correspondait à la méthode est supprimé, mais vous pourriez penser à ce qui est arrivé à toutes les variables locales qui ont été stockées dans le cadre de
pile ? Ils sont tous hors de portée,
ce qui est juste une façon fantaisiste de dire qu'ils sont détruits. Ils sont tous hors de portée, C' est donc un fait important de se souvenir du moment où vous renvoyez hors des méthodes toutes vos variables
locales hors que les méthodes sortent de la portée et sont détruites. Si je continue sur mon programme et retourne aussi hors des premières méthodes, nous sommes de retour à l'endroit où nous avons commencé avec une pile vide. Vous vous demandez peut-être ce qui se passe si une méthode appelle d'autres méthodes, qui provoque une autre méthode, qui appelle une autre méthode 1000 fois bien, la pile se remplirait rapidement avec des cadres de pile jusqu'à ce qu'il y ait de la place normale. Finalement, le stock sera complètement complet sur la pile de Lancers net DOT. Exception de débordement. Si vous voyez ce message d'erreur, cela signifie que vous avez une séquence infinie d'appels de méthode off quelque part dans votre code. Jetons un coup d'oeil à un code. J' ai écrit un programme simple pour dessiner un carré sur l'écran. Vous voyez que j'ai une méthode carrée de tiroir qui appelle une méthode de ligne de tiroir quatre fois pour dessiner les quatre côtés d'un carré. Je vais mettre une pause, points à l'intérieur du tiroir, des méthodes de
ligne, puis courir mon froid. Regarde ça. Maintenant, à ce stade de mon manteau, cette pile ressemblera à ceci. Mon premier appel à dessiner carré est ici au bas de la pile avec ses quatre paramètres, adresse de
retour et les variables locales. Ensuite est l'appel dans une ligne de tiroir avec à nouveau quatre paramètres. Adresse de retour sur. Dans ce cas, aucune variable locale car la ligne de tiroir n'en a pas dans Visual Studio. Vous pouvez jeter un oeil à cette pile en ouvrant la vue de la pile d'appels, qui est ici. Vous pouvez voir l'appel dans le carré de dessin, puis dans une ligne de tiroir. Donc, cette fenêtre vous montre quelles trames de pile sont stockées sur la pile. ce moment, en dernière démonstration, laissez-moi vous montrer une pile. Exception de débordement. Laisse-moi modifier mon manteau comme ça. J' ai modifié mon manteau. Alors dessinez maintenant une ligne appelle dans une ligne de tiroir, qui appelle ensuite dans une ligne de tiroir qui appelle dans une ligne de tiroir. Tu as la photo. Cette séquence ne se terminera jamais. Lorsque je lance ce programme, il va créer une séquence infinie des méthodes de ligne dessinée s'appelant eux-mêmes Exécutons le programme et voyons ce qui se passe. Je suis là, tu l'as. La taille de la pile est limitée. Si j'ai trop de méthodes appelant toutes les méthodes, finalement la pile sera complètement complète sur le framework DOT net flux l'exception de
débordement de pile . Alors qu'avons-nous appris ? Th'll Net Framework utilise la pile pour suivre les médicaments chaque fois que vous appelez le message tous ses paramètres
de méthode. L' adresse de retour sur toutes les variables locales sont placées sur la pile. Ce bloc entier de mémoire est appelé un cadre de pile. Lorsque vous revenez d'une méthode, le cadre de la pile supérieure est supprimé. Toutes les variables locales sont hors de portée à ce stade, et j'ai détruit. Si vous avez une séquence infinie hors méthodes, appelant toutes les méthodes que le stock remplira complètement jusqu'à ce que l'ortie jette une pile. Exception de débordement
4. Qu'est-ce que la mémoire de la mémoire de l'héros ?: l' autre type de mémoire à l'intérieur d'un ordinateur est appelé mémoire à succès ou simplement le tas. Jetons un coup d'oeil à la ligne de code suivante Maintenant. Chaque fois que les mots-clés nouveaux apparaissent en ligne, vous créiez un objet sur le tas. C' est une règle fondamentale. Les objets Internet sont toujours créés sur le tas. Ils ne vont jamais sur la pile. J' ai donc apporté quelques modifications à mon programme carré de tiroir. Jetons un coup d'oeil. Auparavant, les méthodes de ligne de tiroir s'attendaient à ce que quatre paramètres entiers tracent une ligne. Mais maintenant j'ai un tiroir Polygon Methods, qui attend un tableau hors ligne objets dessine tout en une seule fois. La méthode de dessin carré met en place un tableau de lignes avec quatre objets correspondant aux quatre côtés du carré, puis appelle déposer tout un pistolet pour dessiner tout en un. Allez maintenant rappelez-vous, dans mon ancien programme carré de tiroir, j'ai mis un point de rupture à l'intérieur des méthodes de ligne de tiroir et quand j'ai exécuté mes codes, la pile ressemblait à ceci. Mais maintenant, j'ai apporté beaucoup de changements au programme. Alors à quoi va ressembler la pile sur le tas maintenant ? Ok, alors imaginez que j'ai mis des points de rupture dans le tiroir. méthodes polygonales exécutent mon programme. La pile sur le tas ressemblerait alors à ceci. Les lignes froides du paramètre existent sur la pile car c'est un paramètre, mais je l'ai initialisé avec les nouveaux mots-clés. Ainsi, le tableau lui-même est créé sur le tas, donc la variable sur cette pile fait référence à un objet sur le tas. Vous pouvez également voir que le tableau de lignes a quatre éléments et que chaque élément fait référence à un objet de
ligne ailleurs sur le Now, Si vous pensez que tout cela semble beaucoup plus compliqué que le culte précédent, alors vous êtes sous le droit piste sur. Cela m'amène à un point important. Ce code, qui utilise une cave, est légèrement plus lent que mon manteau précédent, qui utilisait des entiers pour tout. Et c'est parce que toutes les références supplémentaires doivent être calculées, du paramètre lines sur la pile au tableau. Instance, sur le tas
, passe d'un tableau éléments du tas à une instance d'objet linéaire ailleurs sur le tas. Donc, l'emporter principale, pour l'instant est les codes qui utilisent des entiers est légèrement plus rapide que le code qui utilise des objets. Alors maintenant que se passe-t-il lorsque la méthode de dessin polygone se termine. Ce cadre de pile est retiré sur les lumières, paramètre est hors de portée et est détruit. Mais voici quelque chose que vous ne vous attendiez peut-être pas. Tableau Z sur la ligne. Les objets sur le tas continuent d'exister maintenant. C' est une situation intéressante. Le paramètre lions est hors de portée, mais les objets sur la chaleur sont toujours là. Nous disons que les objets sont déréférencés en raison de la variable ou du paramètre, qui se réfèrent à eux sans portée. Les objets de référence continuent d'exister et ne sont pas détruits immédiatement. Donc voici une autre importante à emporter. Le framework net DOT sera toujours reporté. Nettoyer les objets référencés à ce sujet est parce que le nettoyage du tas prend beaucoup de temps en le reportant le plus longtemps possible. Votre code fonctionnera plus rapidement, mais finalement le framework devra nettoyer le tas ou nous manquerons rapidement de mémoire. Ce processus de nettoyage est appelé garbage collection, et il se produit périodiquement en arrière-plan. Lorsque le framework commence la collecte des ordures. Il identifie tous les objets sur le tas, qui ne sont plus référencés par aucun paramètre variable ou objets dans votre couche sur son de alloue chacun d'eux. Nous reviendrons à la collecte des ordures dans les conférences ultérieures, et je vais vous donner quelques conseils sur la façon d'éviter les problèmes de performance dans vos codes en raison de la collecte des
ordures. Alors qu'avons-nous appris ? Chaque fois que vous utilisez les nouveaux mots clés dans vos codes, vous créez un objet sur le tas. La variable elle-même peut vivre sur la pile, mais elle fera référence à un objet sur le tas. Le nouveau programme carré de dessin qui utilise un tableau de lignes est légèrement plus lent que l'ancien programme qui utilisait des entiers sur. La raison en est que toutes les références d'objet supplémentaires doivent être traitées lorsque les paramètres et les variables locales de la pile sortent de la portée. Les objets correspondants du tas ne sont pas détruits. Ils continuent d'exister dans un état référencé en D. Le cadre suivant reporte le nettoyage des objets référencés sur le tas aussi longtemps que
possible pour des raisons de performances. Mais finalement, le framework démarrera un processus appelé garbage collection. Comme de alloue tous les objets référencés sur la hanche
5. Quels sont les types de valeurs ?: dans la conférence précédente, nous avons appris à propos de cette pile sur la chaleur. Les connaissances que vous gagnez vous aideront à aller de l'avant lorsque nous examinons les variables, comment elles sont stockées en mémoire par le framework dot net et comment cela affecte les performances de votre code maintenant. Auparavant, quand j'ai parlé de cette pile, j'ai montré du code avec le tiroir. Linus. C' est qui a utilisé quatre paramètres entiers. Examinons de plus près un entier dans le framework Dark Net. Le type insurgé fait partie d'une classe spéciale de types appelés types de valeur, mais quel est le type de valeur ? Le type de valeur est un type de variable, où le type de la valeur de la variable stockée ensemble. Donc, si j'ai une variable entière locale avec une valeur de 12 centaines et 34, ce
type d'interview sur sa valeur sera stocké ensemble comme ceci. Alors jetons un coup d'oeil à cette pile à nouveau. Auparavant, quand j'ai parlé de cette pile, j'ai mentionné tous les types de données que le stocké sur la pile ils sont des paramètres de message. L' adresse de retour d'un message sonne des variables locales. Donc, si j'ai une variable locale off type entier avec la valeur off 12 centaines et 34, elle serait stockée sur la pile comme ceci. Tu vois ça ? Le type et la valeur stockés ensemble sur la pile. Maintenant gardez cela à l'esprit parce que dans la prochaine conférence, je vais parler des types de référence, qui est le stockage différemment. Donc, vous pourriez vous demander quels types dans le framework dark net sont en fait des types de valeur. Eh bien, voici une liste complète. Tous les types numériques sont des types de valeur, y compris tous les points flottants entiers sur les types décimaux. En outre, booléen dans les opérations et les structures sont des types de valeur. Tout le reste dans le framework dot net est appelé un type de référence, dont je vais discuter prochainement. Lorsque les gens essaient d'expliquer la différence entre un type de valeur et un type de référence, vous entendez souvent l'explication suivante. Un type de valeur est un type qui existe sur la pile. Cependant, il s'agit de la valeur de la pièce. Les types peuvent exister à la fois sur la pile sur l'ici, laissez-moi démontrer. Auparavant, quand j'ai parlé du tas, j'ai mentionné quel type de données est stocké sur le tas. Tu te souviens de ce que ça fait ? C' était tous les objets, instances créées avec un nouveau mots-clés en c sharp. Alors imaginez que je crée un objet sur le tas en utilisant les nouveaux mots-clés dans ma couche sur cet objet a un enterré votre champ contenant la valeur 12 centaines. 34. Maintenant, cet entier sera stocké comme ceci. Donc, vous voyez, je sais que le type de valeur sur le tas de sorte que les types de valeur peuvent exister à la fois sur la pile
le jour sur les types de valeur de propriété définissant n'est pas là où ils sont stockés, mais que la valeur est stockée avec le type. Permettez-moi donc de terminer en vous montrant à l'importance des fonctionnalités supplémentaires ou des types de valeur. Disons que j'ai un message dans mes codes avec deux variables A et B. Les
deux sont introduites. La variable A contient la valeur 1234 sous la variable B est zéro. Maintenant, ce qui se passe quand je l'assigne sera Vérifiez ceci. La valeur de A est copiée dans être. Maintenant, c'est une caractéristique importante des types de valeur leurs signes par valeur, ce qui signifie que leur valeur est copiée sur. Alors maintenant que se passe-t-il si je compare A et B ? Il s'agit de deux variables différentes qui contiennent la même valeur. Alors, comment le fera. Le framework dot net interprète bien la situation comme ceci. Le cadre considère que ces deux variables sont égales. Il s'agit de la deuxième fonction importante hors types de valeur. Ils sont comparés par valeur, ce qui signifie que deux variables différentes ayant la même valeur sont considérées comme égales. Alors qu'avons-nous appris ? Les types de valeur stockent leur valeur directement, ainsi que les types de valeur de type peuvent exister sur la pile sur et un type de valeur bissextile sont un signe par valeur, ce qui signifie que la valeur est copiée sur la valeur. Les types sont comparés par valeur. Deux variables ayant la même valeur sont considérées comme égales.
6. Quels sont les types de référence ?: Dans la conférence précédente, j'ai parlé des types de valeur et brièvement mentionné son homologue le type de référence. Mais qu'est-ce qu'un type de référence ? Eh bien, un type de référence est un type de variable qui fait référence à une valeur stockée sur le tas. Pas auparavant. Quand j'ai parlé du tas, j'ai montré mon programme carré de dessin modifié qui avait un dessin une méthode de polygone, si vous vous souvenez avec un paramètre de tableau de ligne, Donc dessiner polygone attendu un tableau hors ligne objets. Examinons de plus près ces objets de ligne juste pour rafraîchir votre mémoire. Voilà encore mon manteau. Vous pouvez voir la définition hors de la classe de ligne ici. C' est un simple conteneur de données avec deux ensembles de coordonnées. Alors imaginez que j'ai un message avec une variable locale hors ligne de type. À quoi ça ressemblerait ? Un bon souvenir, comme ça. Vous pouvez voir que la variable elle-même est sur la pile, mais elle fait référence à une ligne Objects sur le mais une variable de type référence peut-elle également exister sur le tas ? Oui, bien sûr. Tout ce que j'ai besoin de faire est de créer un objet sur le tas. L' utilisation des nouveaux mots-clés a tourné la moitié que les objets ont un champ hors ligne de type. Maintenant, la mémoire va alors ressembler à ceci. Vous voyez qu'ils ont maintenant une variable de type de référence sur le tas, et il fait référence à un objet de ligne, qui est également stocké sur le quoi ailleurs, afin de résumer. Les types de référence peuvent exister sur la pile le jour sur le tas, tout comme les types de valeur, mais ils feront toujours référence à une valeur sur le tas. Permettez-moi de terminer en vous montrant à l'importance des fonctionnalités supplémentaires hors types de référence. Disons que j'ai un message dans mes codes avec deux variables A et B. deux variables sont des lignes. La variable A fait référence à une instance de ligne sur le tas sous la variable B est dit savoir. Maintenant que se passe-t-il quand j'attribue a à B. Vérifiez vous-mêmes La référence de A est copiée dans être. Il s'agit d'une fonctionnalité importante hors types de référence. Ils sont assignés par référence, ce qui signifie que la référence est copiée. Vous vous retrouvez avec deux variables, se référant à la même instance d'objet sur la hanche. Alors maintenant que se passe-t-il quand je compare A et B ? Ce sont deux variables différentes qui font référence aux mêmes objets sur le tas. Comment le framework dot net interprétera bien cette situation comme celle-ci. Le cadre considère que ces deux variables sont égales. Mais attendez, qu'en est-il de ce scénario pour référencer des variables de type pointant vers deux objets distincts sur le tas. Mais les deux objets contenaient des données identiques. Comment le framework dot net interprétera-t-il cette situation Sonne Drinker. Le framework considère ces deux variables comme n'étant pas égales, il s'agit donc d'une autre caractéristique importante hors types de référence. Ils sont comparés par référence, ce qui signifie que deux variables différentes se référant aux mêmes objets sont considérées comme égales. Mais deux variables différentes, se référant à deux objets distincts mais identiques, sont pas considérées comme égales. Alors qu'avons-nous appris ? Les temps de référence peuvent être détectés dans le magasin Types de référence sans valeur, une référence à leur valeur, et cette valeur est toujours stockée dans la référence. Les types peuvent exister sur cette pile le jour du, mais leur valeur est toujours stockée sur la référence. Les types sont attribués par référence, ce qui signifie que la référence est copiée sur la référence. Les types sont comparés par référence. Deux variables se référant aux mêmes objets sont considérées comme égales, et deux variables se référant à des objets distincts mais identiques ne sont pas considérées comme égales
7. Qu'est-ce que la boxe et le boxing ?: dans cette conférence, je vais vous montrer un mystère. Jetez un oeil à ce code. C' est un programme très simple. J' ai commencé avec la variable A contenant la valeur 1234. Ensuite, je déclare une deuxième variable. Ne pas utiliser les objets de type. Andi. J' ai affecté A à B dans C. Sharp. Tous les types héritent d'objets, y compris les entiers, sorte que vous pouvez mettre fondamentalement tout dans une variable de type objet. Mais attendre dans les jurés sont des types de valeur, et les objets sont des types référencés. Donc, en mémoire, mes variables sont stockées comme ceci. Voici ma variable entière A avec sa valeur off 1234. Et voici ma variable d'objet B. Mais B est un type de référence, et nous avons appris dans la conférence précédente que les types de référence font toujours référence à une valeur sur le tas ici. Ce n'est pas possible car A est une variable locale, il existe
donc sur la pile trouvée qu'il s'agit d'un type de valeur, donc sa valeur est également de départ sur le stock. y a tout simplement aucun moyen pour B de se référer à a parce que les deux variables récitent dans différents types de mémoire. Un type de référence ne peut jamais faire référence à la mémoire de pile, donc ce programme ne peut jamais fonctionner. Droit ? Eh bien, celui-là le fait. Je retourne en décembre dans le studio Andi, dirige le programme. On y va. Cool. Ça marche en fait. Mais comment est-ce possible ? Maintenant, c'est bizarre. Basé sur ce que nous avons appris dans les conférences précédentes, ce programme ne devrait pas fonctionner. Mais pourtant, c'est le cas. Comment est-ce possible de le savoir ? Permettez-moi de forcer ce programme dans un langage intermédiaire. En examinant les instructions de langue intermédiaire, nous pourrions trouver un indice. Et voilà. Regarde ça. Enseignement de langue intermédiaire appelé livres. Peux-tu deviner ce qu'il fait ici ? Je vais dessiner sur le tableau. Voici les mises en page de mémoire avec les deux variables A et B. Maintenant, l'instruction des livres le fait. Donc, pour faire fonctionner le programme, le framework Net copie réellement la valeur de l'intervieweur de la pile, puis taxer la valeur dans les objets qu'il place ces objets sur le afin que la variable B puisse ensuite y faire référence. Tout ce processus s'appelle la boxe. La boxe arrive chaque fois dans les coulisses. Lorsque vous avez un paramètre variable champs ou propriété hors temps objets et que vous affectez un type de
valeur à son boxing est agréable car il brouille en quelque sorte la ligne entre les temps de valeur et types de
référence. Mais la boxe peut aussi être douloureuse car elle introduit des frais généraux supplémentaires dans votre code. Maintenant, vous vous demandez peut-être s'il existe un processus correspondant appelé unboxing. Oui, il y en a. Voici encore mon programme. Jetons un coup d'oeil à la dernière ligne. Je déclare une variable, voir off type entier et assigne la valeur de l'objet à son en utilisant un typecast. Un autre peu de magie. En fait, parce que voir, existe sur la pile et être les objets fait référence à un objet sur le dans la
langue intermédiaire . L' instruction correspondante est ici. Ça s'appelle Unbox. Laissez-moi revenir au tableau noir sur Dessiner le processus Unboxing. Nous avons commencé à partir de la situation de boîte avec l'entier sur le Maintenant. Alors ça arrive. Unboxing décompresse l'entier sur le tas et copie la valeur dans la variable Voir sur la pile. déballage se produit chaque fois dans les coulisses lorsque vous disposez d'une valeur d'objet et que vous la convertissez en un type de valeur. boxe et le déballage peuvent affecter sérieusement les performances de votre manteau, alors assurez-vous de l'éviter autant que possible dans les sections critiques de la mission. Je vais partager quelques conseils et astuces dans les conférences ultérieures sur la façon de le faire. Qu' avons-nous appris ? boxe est le processus de prise de types de valeur sur la pile, les
empaqueter dans des objets comme placer ces objets sur le tas. boxe se produit chaque fois que vous attribuez un type de valeur à un paramètre variable de champ ou de propriété hors type objets. Le déballage est le processus inverse. Les objets hors du tas sont décompressés sur la valeur. types à l'intérieur sont copiés dans la pile. Apprendre la boxe se produit chaque fois que vous n'avez pas de valeur d'objet et que vous lui lancez un type de valeur. boxe et le déballage ont nui à la performance de votre manteau et devraient être évités dans les sections critiques.
8. Quelles sont les chaînes de caractères immutables ?: dans cette conférence. Nous allons jeter un oeil à la classe de chaîne dans dot net maintenant. Selon vous, qu'est-ce qu'une chaîne de type valeur ou un type de référence ? Allons le découvrir. J' ai écrit à travers des codes pour tester ce qu'est réellement une chaîne. Vérifie ça. Je commence par déclarer la variable String a lors de l'initialisation C'est à la valeur ABC. Ensuite, je déclare une deuxième variable de chaîne B et un signe A à B. Dans la ligne suivante, j'ajoute un seul caractère à la fin du flux be et finalement j'écris les deux chaînes à la console. Maintenant, si les chaînes sont des types référencés, la pile et le tas ressembleraient à ceci. Je devrai les variables A et B, toutes les deux pointant vers les mêmes objets de chaîne sur le tas. Ensuite, si je modifie la variable de chaîne B, la chaîne invariable A serait également modifiée parce que les deux variables font référence à la même chaîne. Cependant, si les chaînes sont des types de valeur, le deuxième tas ressemblerait à ceci. Je devrais variables a et B contenant des chaînes séparées lorsque je modifie la variable de chaîne B. L'autre chaîne invariable A n'est pas affectée. Quel scénario est juste. Pensez-vous qu'on le découvre en exécutant mon code. On y va et là tu l'as. Les chaînes sont évidemment des types de valeur, non ? Eh bien, aucune chaîne ne sont en fait des types référencés en mémoire. Les choses ressemblent à ça. Voici les deux variables A et B, toutes deux se référant à la même chaîne sur le tas. Mais quelque chose de spécial se produit lorsque je modifie la variable de chaîne B au lieu de modifier la chaîne sur le tas directement. Ça arrive. Nous disons que les cordes sont immuables. Objets sur le Net. Toute modification apportée à une chaîne entraîne la création d'une nouvelle chaîne dans le flux d'origine est laissée intacte. Pour cette raison, les chaînes se comportent comme si elles étaient des types de valeur. Alors, quels sont les avantages d'avoir des chaînes immuables menacent la sécurité, chaînes
immuables ? Une menace sûre ? Parce qu'ils ne peuvent pas être modifiés économies de mémoire. Des chaînes identiques peuvent être fusionnées en toute sécurité. Ceci est appelé lors de la rotation de la première affectation pour copier une chaîne. Tout ce que vous devez faire est de copier la référence au lieu d'avoir à copier tous les caractères un par un. Et les chaînes internes de première comparaison peuvent être comparées en comparant la référence au lieu d' avoir à comparer tous les caractères un par un. Jetons donc un coup d'oeil aux codes intermédiaires pour voir ce qui se passe dans les coulisses. Je place un point de rupture sur la dernière ligne de mon programme. Démarrez le programme, puis passez aux codes intermédiaires. La première ligne est là. Une chaîne avec du contenu. ABC est chargé sur l'à l'aide de l'instruction de chaîne de chargement sur les magasins invariables A avec une instruction d'
emplacement de magasin . Zen A est assigné à être en chargeant simplement la référence invariable A avec un emplacement de charge sur le stockage à la variable B avec un emplacement de magasin, une chaîne ultra-rapide assignations par référence exactement ce que nous attendrions pour un
type immuable . La magie se produit lorsque je modifie la chaîne invariable être dans les coulisses. Le framework appelle une méthode concussed par chaîne. Laissez-moi regarder cette méthode en utilisant le navigateur d'assemblage dans le studio d'été. C' est là. Vous pouvez voir que le désordre de chat peut il est crée une nouvelle chaîne ici. Il copie ensuite les deux arguments de chaîne dans la nouvelle chaîne et renvoie une référence à la nouvelle chaîne ici. Donc là, vous l'avez. Au lieu
de cela, en modifiant cette chaîne invariable, les codes créent un flux entièrement nouveau, copie tout dans son et stocke une référence à la nouvelle chaîne. Invarable Être l'ancienne chaîne dans B est laissée sur le tas dans un état référencé deal attendant d' être récupéré. Toutes les méthodes de la classe de chaîne qui modifie la chaîne de quelque manière que ce soit ont ce comportement. Au lieu de cela, modifiez directement le flux. Il crée une nouvelle chaîne entière et place les modifications. Là-bas. La chaîne d'origine est laissée intacte. Alors qu'avons-nous appris ? Les chaînes sont des types référencés sur et immuables. Pour cette raison, les chaînes se comportent comme si elles étaient des types de valeur. Ce sont des signes et des comparaisons par valeur. Les chaînes immuables sont des menaces sûres dans les cordes musicales sont rapides car elles peuvent être signées et comparées par référence. Les chaînes immuables économisent de la mémoire car les chaînes identiques peuvent être fusionnées en mémoire.
9. Un cours accéléré en langue intermédiaire: dans cette conférence, je vais parler de la langue intermédiaire. Alors, qu'est-ce que c'est exactement la langue intermédiaire ? Eh bien, laissez-moi commencer par vous montrer un compilateur classique, dans ce cas un C ou C plus, plus un compilateur. Au sommet. Sur les tableaux noirs, vous voyez un simple morceau de code, une boucle quatre qui ajoute un entier à un résultat froid variable. Un compilateur
A, C ou C plus plus prendrait ces fragments de couche source et le compilerait langage
machine, qui est fondamentalement juste un tas de nombres stockant la mémoire sur exécutée par le CPU dans votre ordinateur mais un darknet compilateur. Poussez les choses différemment. Jetons un coup d'oeil au même morceau de code écrit dans C. Sharp. Un compilateur C pointu va d'abord compiler la côte source vers un langage intermédiaire spécial appelé Eh bien, vous avez deviné ce langage intermédiaire commun. C i, l ou soie. La même couche est stockée dans un fichier dll ou xer. Lorsque le code est exécuté, un autre compilateur démarre. Ce compilateur est appelé un compilateur jit, ou G i T G I T signifie juste à temps. Le compilateur s'exécute juste à temps aux derniers moments possibles avant que les codes
ne doivent être erronés . Le compilateur Jit prend le langage intermédiaire et compile le langage machine toe. Alors pourquoi ce processus compliqué de compilation en deux étapes ? Eh bien, parce qu'il a en fait un certain nombre d'avantages importants. Tout d'abord, le manteau de soie peut être optimisé pour la plate-forme sur laquelle il fonctionne. Donc, les différences entre, par
exemple, un M D sur Intel. L' utilisation du processeur peut être pleinement exploitée par le compilateur jit pour créer un
langage machine rapide et optimisé . Secondes. Le froid bientôt est entièrement portable car il n'est pas lié à une plate-forme matérielle particulière. Le manteau peut fonctionner sur Windows sur Lenox sur les ordinateurs Apple. En fait, j'ai créé tout ce cours sur un Apple MacBook Pro et tous les exemples de code. Vous allez voir un fonctionnement natif sur OS X, mais vous pourriez prendre ces mêmes bols exécutés que je cours, les
copier sur des mèches ou des fenêtres et les exécuter là. Ça marcherait. Un autre avantage utile sur le manteau de soie est qu'il peut être très combat avant de courir pour
s'assurer que le manteau n'effectue aucune opération dangereuse et enfin, encore froid peut être annoté avec des métadonnées, par
exemple, instructions de sérialisation disant froid externe exactement comment convertir vos classes toe XML et retour. Y a-t-il des inconvénients à utiliser un manteau de soie ? Eh bien, oui. L' utilisation d'une copulation en deux étapes est légèrement moins efficace que la compilation directe des
codes source vers le langage machine. Donc compilé. Le Net rhumes est légèrement plus lent que le C ou C compilé directement plus les mégots de code. Les compilateurs l'ont causé vraiment bon au cours des deux dernières années, et donc cette différence est devenue microscopique. Aujourd'hui, il est presque impossible de remettre un langage machine culte qui est plus efficace que
ce que produit le compilateur C Sharp. Regardons donc la langue intermédiaire plus en détail. Comment fonctionne réellement la langue ? La langue intermédiaire est basée sur trois concerts. La première est la séquence des instructions affichées sur les écritures sur le tableau noir. Les programmes de langue intermédiaire contiennent, ou les instructions de mise en marche qui sont exécutées dans l'ordre, tout comme un programme pointu en C. Les variables locales dans un programme de langue intermédiaire, un stocké dans des emplacements spéciaux appelés emplacements. Il y a un certain nombre d'emplacements disponibles. J' en ai dessiné quatre sur le tableau noir à gauche. Enfin, il y a une évaluation Stack a stack est une collection de valeurs avec deux opérations de base, un push appelé Load in Ill, qui ajoute de la valeur à la pile et pousse tout le reste vers le bas d'un niveau, l'autre est un pop appelé Store in Ill, qui supprime la valeur supérieure de la pile sur les mouvements pleins. Autres valeurs jusqu'à un niveau. Il y a trois groupes hors instructions dans les instructions de langue intermédiaire qui ont poussé les données sur l'évaluation. Instructions bloquées qui effectuent des opérations sur la pile sur les instructions qui affichent des valeurs de la pile. Alors jetons un coup d'oeil. C' est un programme très simple. Jetez un oeil aux deux lignes de code suivantes, j'ai initialisé un entier avec la valeur 456 ,
puis en ajoute une à cette valeur. À quoi ressemblerait ce programme en langue intermédiaire ? Eh bien, ce programme était composé de seulement quatre instructions de langue intermédiaire. La première instruction est charge constante. Un, qui charge la valeur entière signée de quatre octets. Un sur la note coincée d'évaluation qu'il n'y a qu'une seule variable locale dans mon programme appelé I. Et donc il est stocké dans l'emplacement zéro, avec la valeur initiale de 456. L' instruction suivante est charge Location zero, qui charge la valeur de la variable I dans l'emplacement zéro sur l'évaluation bloquée. Donc maintenant, nous avons deux numéros du stock un et 456. La troisième instruction est tante, qui ajoute les deux premiers chiffres sur la pile ensemble. Donc, le résultat est 457 qui va sur la pile et remplace les deux numéros originaux. Donc maintenant, nous avons un seul numéro sur la pile. 457. Le début final est le magasin Location zéro, qui sort la valeur supérieure du stock d'évaluation et le stocke dans Location zéro, ce qui correspond à la variable I. Voici quelques autres instructions que vous pourriez rencontrer lorsque vous regardez compilé C manteau pointu. Les livres et les instructions de déblocage font exactement ce que vous pouvez attendre. Ils encadrent puis répertorient les types de valeur sur l'instruction B et E signifie branche, pas égal. Il saute à une couche de localisation différente. Si les deux premiers nombres sur le stock d'évaluation ne sont pas égaux, cool et appelé virtuel, appelez statique sur les membres de classe non statiques, chargez les éléments et stockez les éléments dans une course dimensionnelle. Nouveau tableau en tant que nouveaux objets crée un nouveau tableau, respectivement. Les nouveaux objets sur le retour revient à partir d'une méthode et lancer une exception. Donc, vous avez peut-être pensé que le langage intermédiaire est très complexe, mais c'est en fait assez simple. Le langage n'est pas si difficile à comprendre, et dans les prochaines conférences allaient jeter un oeil à compiler, voir beaucoup d'odeurs de Shark et analyser comment le compilateur traduit son code source pointu en langage
intermédiaire sur ce que le les incidences sur le rendement sont. Jetons un coup d'oeil au programme simple. Jetez un oeil à mon code ici. Je commence par le numéro deux, puis utilise cette boucle quatre pour calculer les 16 premières puissances de deux en multipliant à plusieurs reprises le nombre par deux. Alors, comment cela ressemble-t-il dans un langage intermédiaire ? Eh bien, tout ce que j'ai à faire est de définir un point de rupture à la fin du programme ici, exécuter mon programme, puis passer à la vue de démontage comme ceci. Nous regardons maintenant le manteau de langage intermédiaire compilé annoté avec des lignes du code source
original afin que nous puissions facilement trouver les colts compilés pour une ligne donnée de codes
pointus C . Commençons donc par la déclaration de la variable de nombre. Vous voyez ici une constante de charge à l'instruction, qui pousse le numéro deux sur la pile, puis un point de magasin zéro, qui stocke le nombre dans l'emplacement zéro. Nous savons donc que l'emplacement zéro correspond à la variable de nombre. Puis vient le forum. Maintenant, faites attention aux étiquettes d'adresse ici parce que les codes sont affichés hors de l'ordre pour le rendre plus facile à lire. La première étape consiste à initialiser la variable I 20 Donc, nous avons une constante de charge zéro sur et stocker l'emplacement une instructions ici. Donc maintenant, nous savons que la variable I est stockée dans l'emplacement un. Ensuite, le culte saute à l'emplacement 14. Là, nous avons un emplacement de chargement un, qui est le très bien. Je charge Constant 16 qui charge le numéro 16 sur, puis une instruction BLT. Maintenant BLT signifie branche moins qu'on. Il sautera à l'emplacement A. Si la variable I est inférieure à 16. L' emplacement A contient le corps principal hors de la boucle. Il charge la variable de nombre sur la pile. Ensuite, il charge le numéro deux sur la pile. Il multiplie les deux nombres sur la pile ensemble et stocke les résultats dans la variable de
nombre parce que le code est en panne. La prochaine instruction après l'emplacement F est en fait ici à l'emplacement 10. Nous sommes de retour où la variable I est incrément ID a testé. Si elle est inférieure à 16 était la variable. J' atteint la valeur 16. Nous allons compléter l'instruction BLT ici. L' emplacement suivant après l'emplacement 17 est un. Voir sur l'instruction de retour retourne hors de la méthode, et là vous l'avez. Un programme de cinq lignes C pointu compile à seulement 17 instructions de langue intermédiaire sur le froid
compilé était assez facile à lire, donc félicitations vous êtes maintenant en mesure de lire compilé. Voir manteau de requin. Être capable de lire et d'interpréter des codes de langue intermédiaire est une compétence très importante, qui vous aidera beaucoup lorsque vous optimisez les performances de votre manteau. Alors qu'avons-nous appris ? C. Sharp Coats compile en langage intermédiaire, qui est ensuite compilé à nouveau par un compilateur juste à temps. Deux langues machine Gits Copulation peut optimiser les codes pour le matériel local dans les codes de
langue des médias , est portable et peut fonctionner sur plusieurs plates-formes comme Windows, Linux et Mac. Il peut être très combat pour l'exactitude avant de courir, et l'entraîneur peut être annoté avec des métadonnées supplémentaires, par exemple,
pour guider la sérialisation. Intermediate Language utilise des emplacements pour stocker des variables locales et utilise une pile d'évaluation pour effectuer des opérations sur les données. Intermediate Language a intégré la prise en charge de la création d'objets appelant des méthodes sur l' accès aux champs. Le langage intermédiaire a intégré un support pour la création et la manipulation d'une
élévation dimensionnelle .
10. Astuce #1 : Ne PAS l'enfiler la boîte et le déboxer: précédemment, je vous ai dit que la boxe et le unboxing ont un effet négatif sur les performances de votre code, et vous devriez l'éviter autant que possible, en
particulier dans les sections critiques de la mission. Mais quelle est la mauvaise performance hors boxe et unboxing ? C' est pire l'inquiétude ? Voyons que j'ai écrit un programme pour mesurer la différence de performance entre une méthode qui n'utilise que des jobs inter contre et des méthodes qui font exactement la même chose en utilisant une
variable d'objet . Voici le code. J' ai deux méthodes. Mesurer la mesure d'un homme B. Le premier 1 mesuré a prend un entier et ajoute la valeur un à 1 000 000 fois. J' utilise un orteil de classe chronomètre mesure avec précision le nombre de millisecondes qu'il faut pour exécuter la boucle. Le deuxième message Mesure B, fait exactement la même chose mais utilise maintenant une variable de type objet au lieu d'un entier. Tout le reste est pareil. Un million de répétitions sur j'ajoute le numéro un au cours de chaque boucle. C' est l'oration. Si je fais défiler jusqu'aux méthodes principales, vous voyez que je commence en appelant les deux méthodes de mesure et en rejetant les résultats. Je fais cela parce qu'il pourrait y avoir un début de retard dans mon manteau qui pourrait fausser les résultats
de mesure. Donc, pour éliminer ce retard, je fais le test complet. Une fois que les résultats ont été découverts, puis exécutez à nouveau le test. Enfin, j'affiche les résultats en millisecondes sur la console pour les deux méthodes. Je suppose que c'est la méthode A qui sera plus rapide. Donc, j'affiche aussi combien de fois la méthode B est plus lente que la méthode A. Non, je vous ai déjà dit la boxe et une boxe introduit une performance significative. Les frais généraux dans votre froid. Voyons de combien je parle de frais généraux. Laisse-moi diriger le programme maintenant. Là, vous l'avez. Le message A, qui utilise une variable entière, prend 10 microsecondes. La méthode B, qui utilise une variable objet, prend 55 microsecondes. La méthode B est 5,5 fois plus lente que la méthode A. Jetons un coup d'oeil au code intermédiaire. Je vais commencer par l'ajout d'entier dans la méthode A. Si j'ai dit deux points de rupture et que j'exécute, puis passer à la vue de démontage. On y va. Vous voyez que le code intermédiaire est annoté avec du code source C tranchant, il est
donc vraiment facile de localiser la ligne que nous voulons. Dans ce cas. L' ajout dans la méthode A. Voici maintenant, avant d'expliquer ce que font les instructions de la méthode, gardez à l'esprit que les codes intermédiaires utilisent une pile d'évaluation spéciale pour effectuer des calculs. nombres et les variables sont chargés sur cette pile en utilisant des instructions de chargement, en commençant par LD sur les résultats sont stockés dans des variables
avec des instructions de magasin commençant par S t. en magasin en couche intermédiaire . Voici les quatre instructions. Le 1er emplacement de chargement. On charge la première variable locale, qui se trouve être la variable A sur la pile d'évaluation. Vient ensuite la constante de charge une instruction, qui charge le numéro un jusqu'à la pile d'évaluation. L' instruction add ajoute les deux premiers nombres sur la pile ensemble, dans ce cas, la valeur d'un sous numéro un. Et il remplace ces deux chiffres par les résultats de l'addition. Enfin, l'emplacement du magasin, un magasin d'instructions. L' addition résulte dans la variable A. Maintenant, regardons la même ligne dans le message. Utilisez la variable objet. Ici, c'est encore une fois. Nous commençons par l'emplacement de chargement un, qui charge la variable d'objet A jusqu'à la pile d'évaluation. Maintenant, gardez à l'esprit qu'une variable objet est un type de référence, donc nous avons maintenant une référence à un objet sur le tas sur le stock d'évaluation. Donc, l'instruction suivante doit être sur l'instruction de déballage car les objets sur la chaleur doivent être déballés sur la valeur à l'intérieur doivent être copiés jusqu'à ce que la pile d'évaluation. Vient ensuite la constante de charge familière, qui charge le numéro un, puis l'instruction d'ajout pour ajouter les deux valeurs ensemble. Non a est une variable d'objet, donc les résultats de l'addition doivent être emballés dans un objet et placés sur le tas . Donc, l'instruction suivante doit être boîte, qui fait exactement cela sur les lieux, une référence à ces nouveaux objets sur le tas sur l'évaluation coincé. Enfin, nous avons l'emplacement de magasin un qui stocke le nouveau jour de référence d'objet invariable. Donc méthode une méthode être contenu à peu près la même couche, une constante de charge de l'emplacement faible, puis une annonce et enfin, un emplacement de magasin. Mais la méthode B a besoin d'un unbox supplémentaire sur les instructions de boîte parce que nous effectuons sur interred. Votre ajout sur le type de référence de capable sur le framework net DOT doit déplacer
les données entre cette pile et le tas pour que cela fonctionne. Donc, la différence de performance est entièrement due à ces deux boîtes d'instructions sur Unbox. Éviter la boxe sur unboxing dans votre propre code est facile. Évitez simplement le type d'objet. Mais saviez-vous que le framework Net est plein de classes qui utilisent des objets ou des objets ? Baies pour le stockage interne, par
exemple, Presque tout le système fait. L' espace de noms des collections utilise l'objet d'une course en interne, donc gardez-le à l'écart des classes suivantes dans le code critique de mission. Une autre classe populaire est la table de données dans les tables de données de points système stockent la valeur chaque ligne dans un tableau d'objets. Même les tables de données de types ne sont pas sûres à utiliser car elles ont utilisé des moulages de type au-dessus de ce même tableau d'objets. Donc, pour garder votre code rapide, envisagez d'utiliser des classes de collection génériques dans le système. Les collections ne sont-elles pas d'espace générique pour les noms ? Ou si vous connaissez le nombre d'éléments à l'avance, envisagez d'utiliser un simple tableau de types à
une dimension pour les tables de données. Il n'y a pas d'alternative simple. Si vous pouviez vous en sortir, re facteur vos codes pour utiliser un lecteur de données car ils sont beaucoup plus rapides qu'une table de données. Mais rappelez-vous, une table de données est les résultats d'une opération de base de données, qui peut prendre
plusieurs millisecondes à terminer. n'y a pas beaucoup de points pour optimiser la récupération des données qui vient après. Sauf si vous faisiez des millions d'opérations le même jour, les objets de table. Alors qu'avons-nous appris ? Moulage d'objets, Variables en types de valeur ? Introduit sur l'instruction unbox dans la couche intermédiaire stockant les types de valeur dans
les variables d'objet introduit une instruction de boîte dans les codes intermédiaires. Les codes avec des livres sur des livres s'exécutent jusqu'à cinq fois plus lentement que le même code sans instructions, vous devriez éviter de lancer vers et depuis un objet dans le code critique de mission. Vous devez éviter d'utiliser des classes de collection non génériques dans le Code critique de mission sur. Et vous devez éviter d'utiliser des tables de données dans les codes critiques de mission, mais uniquement si vous effectuez de nombreuses opérations sur la même table de données. Objet
11. Astuce #2 : Ajouter des chaînes ensemble efficacement: dans cette conférence, je veux regarder de plus près la concaténation de chaînes ou comment ajouter des chaînes. Ensemble, il y a deux façons d'ajouter des chaînes ensemble. Le premier est celui que nous utilisons tous correctement partout dans nos codes. Nous avons appris à chaîner des variables ensemble en utilisant l'opérateur plus. Jetons un coup d'oeil à un code. J' ai fait une application pour mesurer la performance en ajoutant des chaînes ensemble. Je commence avec une chaîne vide ici, puis dans cette boucle, j'ajoute un seul caractère à la chaîne 10 000 fois. Non, Si je lance le programme, vous pouvez voir les performances ici dans la fenêtre de sortie en millisecondes. Mais il y a une autre façon d'ajouter des chaînes ensemble. Le cadre de la pâte Net fournit une classe spéciale appelée constructeur de cordes pour construire des
chaînes de puits . Si j'écris la même couche en utilisant un constructeur de chaînes, commençant par une chaîne vide, en ajoutant un seul caractère 10 000 fois le code ressemble à ceci à peu près le même excès que j'utilise les méthodes stylos au lieu de l'opérateur plus. Mais l'effet est le même sur l'ajout de chaînes ensemble. Non, laissez-moi rare les codes de mesure pour cette deuxième méthode sur et relancer le programme afin que nous puissions comparer les résultats. Chicken fait la concaténation de chaîne en utilisant l'opérateur plus prend 242 millisecondes, mais les mêmes codes utilisant un constructeur de chaîne ne prennent qu'une millisecondes. Le constructeur de chaînes est 242 fois plus rapide. Maintenant, je dois faire un petit avertissement. Lorsque vous répétez cette expérience sur votre propre ordinateur, vous verrez des temps plus rapides, probablement dans la gamme de centaines et 50 à 200 millisecondes. La raison de mes performances plus lentes est que je suis en train d'exécuter un programme d'enregistrement d'écran en arrière-plan en ce moment sur ce qu'il mange des cycles CPU sur l'allocation de mémoire disponible. Rappelez-vous la conférence sur les cordes immuables ? J' ai montré que la classe de chaîne est une classe immuable, ce qui
signifie que toute modification de la chaîne crée une nouvelle chaîne. Cela rend la classe de chaîne se comporter comme un type de valeur, même si c'est en fait un type de référence. Qu' est-ce qui se passe en mémoire pendant le Luke quand j'ajoute à la chaîne ? Voici sur les tableaux noirs les 3 premières itérations de la boucle. Vous pouvez voir que c'est un processus super inefficace. Chaque fois que je tante, un personnage à la corde. La chaîne entière est copiée vers une nouvelle chaîne sur le tas. Mon manteau fait 10 milliers d'opérations de copie de mémoire dans les coulisses. Un autre gros inconvénient de ce manteau est qu'il laisse une trace hors des
objets de chaîne référencés sur le tas pour 10 000 itérations. Le code laisse 9999 objets de chaîne morts derrière. Le collecteur de déchets va avoir beaucoup de travail pour nettoyer tout ça maintenant. Contrastez ceci avec un constructeur de chaînes. Un constructeur de chaînes utilise un tableau de caractères sur une certaine longueur par défaut et écrit simplement du texte dans le tableau. Donc les 3 premières itérations ressemblaient beaucoup mieux, tu
ne crois pas ? Chaque ajout de chaîne écrit un caractère dans votre A. Ceci est beaucoup plus efficace, et quand nous avons fini avec le flux, nous avons simplement déréférencé Le constructeur de chaînes sur le garbage collector n'a besoin que de nettoyer un objet unique, donc voici le principal enlever de cette conférence. Si vous ajoutez des chaînes ensemble dans votre code, utilisez
toujours un générateur de chaînes. Essayez d'éviter d'ajouter des chaînes en utilisant l'opérateur plus. Je ne fais pas mes mesures précédentes en effectuant 10 000 ajouts. Mais comment ce constructeur de chaînes s'empile-t-il quand je ne fais qu'un ou deux ajouts ? Voyons d'après ce qu'il trouve mes codes comme ça. J' ai deux boucles maintenant. Le premier look ne fait qu'un nombre limité. Éditions de cordes 2 à 19. La boucle externe répète cette expérience 10 000 fois. Andi additionne le temps total. En millisecondes, I génère le nombre d'ajouts 2 à 19 et pour chaque addition compte. J' affiche le nombre total de millisecondes pour les chaînes régulières sur pour le constructeur de chaînes. Laisse-moi diriger le programme. Ici, nous allons là-bas pour jusqu'à quatre ajouts. Les chaînes régulières sont en fait plus rapides que les constructeurs de chaînes au-dessus de quatre ajouts. Le constructeur de chaînes est plus rapide. Voici la sortie du programme, tracée sous la forme d'un graphique linéaire. La ligne bleue du graphique correspond aux performances des éditions de chaînes régulières. Vous pouvez voir que les chaînes irrégulières surpassent les constructeurs de chaînes jusqu'à quatre éditions. C' est cinq ajouts ou plus. classe de constructeur de chaînes devient plus efficace sur les ajouts que vous faites, Plus
les leads de performance des constructeurs de chaînes deviennent maintenant. La raison de ce comportement est que la classe de générateur de flux a un peu de surcharge dans la
configuration de son terrain de caractère interne, en gardant une trace des ailes de chaîne sur. Et c'est la capacité de mémoire tampon interne sur l'extension de ce tampon lorsque nécessaire. Les cônes qui utilisent des chaînes régulières juste avant une séquence d'opérations de copie de mémoire pour
implémenter les ajouts. Ceux-ci sont en fait plus efficaces que le constructeur de chaînes, mais seulement jusqu'à un point, et ce point est exactement quatre éditions. Alors, comment devriez-vous faire la concaténation de chaîne ? Eh bien, si vous avez quatre ajouts ou moins, il n'y a rien de mal à utiliser des variables de chaîne régulières sur l'opérateur plus. Cela vous donnera réellement la meilleure performance si vous allez ajouter plus de quatre chaînes ensemble ou vous ne connaissez pas le nombre d'ajouts à l'avance utilisé pour string Builder Au lieu de cela, une fois que vous obtenez jusqu'à des milliers de rabais sur les ajouts ou, dans ce cas, 10 milliers d'éditions, exactement ce constructeur de chaînes est en fait plus de 240 fois plus rapide que les
ajouts de chaîne réguliers . Donc, dans ce scénario, toujours utiliser le générateur de chaînes
12. Astuce #3 : Utilisez le cours de liste correct: si nous voulons stocker une collection de valeurs. Il existe de nombreuses possibilités dans le framework dot net. Nous avons les classes de collection dans les collections de points système. Nom de l'espace. Nous avons des collections génériques dans le système. Est-ce que les collections. Est-ce que le nom générique espace Onda. Nous avons des types réguliers une course. Alors quelle approche est la plus rapide ? Allons le découvrir. Laisse-moi passer à mon manteau. Je commencerai par mesurer la performance souvent la liste des tableaux. J' ai une boucle ici qui ajoute un million d'entiers à la liste des tableaux. En bas, voici les secondes ratées qui ajoutent aussi un million d'insurgés. Mais ici, il utilise la liste générique off type entier pour stocker les valeurs. Exécutons les codes pour comparer les performances. Ici, nous allons sur les résultats. Loin loin, les codes de la liste de tableaux prennent 254 millisecondes, alors que la liste générique ne prend que 42 millisecondes. La liste générique est six fois plus rapide. Pour comprendre la raison de cela, jetons un oeil aux dispositions de mémoire pour Ray List. Après trois ajouts, la pile et le tas ressemblaient à ceci. Maintenant, si vous vous souvenez de la conférence sur la boxe et le déballage, j'ai mentionné que de nombreuses collections dans les collections de points système nomment l'espace des objets, des tableaux pour le stockage interne. La liste du tableau ne fait pas exception, donc chaque élément de la liste du tableau est une référence à un entier encadré, qui est ailleurs sur le tas. On a vu que la boxe et l'Unboxing introduit une performance significative. frais généraux à votre manteau. Maintenant, comparez ceci avec la mise en page de la mémoire d'une liste générique. Encore une fois, la liste est sur la chaleur, mais maintenant les inter jurés sont également stockés directement à l'intérieur des éléments de la liste. Et cela est parce qu'une liste générique hors type entier utilise un tableau entier natif pour le stockage
interne sur un tableau d'objets. Cela élimine tous les objets. Les références des éléments de liste sur supprime également le besoin de boxe. Donc, l'essentiel à retenir est toujours utiliser des listes génériques et des classes de collection sur Évitez les classes dans l'espace de noms des collections de points système pour les codes critiques de mission. Maintenant, essayons un costume Alternative. Un tableau d'entiers natifs pré-initialisé à un million d'éléments. Je boucle sur chaque élément du tableau et définit la valeur un par un. Laisse-moi comparer ce manteau aux deux autres. On y va maintenant. C' est beaucoup plus rapide. L' exécution du tableau natif ne prend que 11 millisecondes. Alors que la liste génétique nécessite 52 millisecondes, le tableau natif est presque cinq fois plus rapide que la liste générique. La raison de cette excellente performance est que le langage intermédiaire que le compilateur C sharp compile,
aussi, aussi, a un support natif pour une course. Jetons un coup d'oeil à mes compilations froides. Je vais définir un point de rupture, relancer le programme, puis passer en vue de démontage. Nous continuons, et voici la mission. Comme vous pouvez le voir, la référence au tableau est d'abord chargée sur la pile d'évaluation. Ensuite, le tableau décalages dans l'élément que nous voulons écrire est chargé sur la pile . Enfin, la valeur qui doit être définie sur les éléments est ajoutée à la pile. Et puis vient une instruction Set Elements, qui écrit la valeur dans les éléments de tableau spécifiés. Nous avons donc une instruction dédiée appelée « set elements » pour écrire des valeurs dans une course. Aucun code utilisant un tableau n'est rapide. Maintenant. Vous pourriez être en train de couler que je triche. Parce que la liste de tableaux et les classes de liste génériques utilisées sur le tableau interne
redimensionnent continuellement ce tableau quand il déborde, alors que mon tableau d'intervieweur était déjà pré-initialisé à un million d'éléments. Donc, ce ne sont jamais des débordements. Laissez-moi donc modifier mes codes pour supprimer ces frais généraux. J' irai à la ligne ici où la liste du tableau est initialisée. J' ai mis dans une capacité par défaut d'un million d'éléments. Ensuite, je descends sur cette ligne. Je fais la même chose pour la liste générique. Maintenant, laissez-moi exécuter le code à nouveau. Vous voyez, la liste générique ne prend plus que 31 millisecondes pour s'exécuter, ce qui est 40 % plus rapide que l'exécution précédente. La liste des tableaux prend 187 millisecondes, ce qui est une amélioration de seulement 26 %. La baie native prend 12 minutes. Secondes est toujours l'option la plus rapide, 2,5 fois plus rapide que la liste générique. Le support intégré pour les bérets dans la langue intermédiaire garantit qu'ils
surpassent toujours les classes de collection. La liste générique et la liste de tableaux ne peuvent tout simplement pas rivaliser avec ce rôle Performance. Voici un graphique Avec tous les résultats de performance, la liste de tableaux passe la plupart de son temps à boxer enterré vos données pré dimensionnement le plus fragile travers le nombre correct d'éléments augmente les performances de 40% mais ce n'est toujours pas très de bons résultats. La liste générique est beaucoup plus rapide car elle peut stocker l'introduction directement dans les
éléments de liste sur le tas sans avoir besoin d'une référence de boîte supplémentaire et pré-dimensionner la liste au bon nombre. Les éléments Off augmentent les performances de 26 %. La baie native reste l'option la plus rapide. Même avec des listes précises, le tableau est encore 2,5 fois plus rapide que la liste génétique. Alors, quand devriez-vous utiliser une course ? Eh bien, si vous avez des codes critiques sur le nombre d'éléments hors est connu à l'avance en utilisant tableau. Si vous avez des codes critiques sur le nombre hors éléments n'est pas connu à l'avance, utilisez une liste générique. Évitez les classes dans les collections de points système. Ils utilisent la boxe sur notre remplacé par les classes génériques dans les collections DOT système ne sont pas génériques
13. Astuce #4 : Utilisez le bon type de tableau: Dans la conférence précédente, nous avons examiné les collections génériques et non génériques et les avons comparées à l'augmentation native. Il s'est avéré que les tableaux natifs sont les plus rapides, mais les collections génériques ont l'avantage qu'elles se développent automatiquement que vous les
éléments Portmore en eux. Donc, si le nombre d'éléments off est connu à l'avance et array est l'option la plus rapide, mais le framework dot net prend en charge en fait trois types d'une course. Le premier type est le tableau unidimensionnel que nous avons vu dans la conférence précédente. Tout ce qui est déclaré avec les crochets de rayons carrés dans C Sharp est un tableau unidimensionnel, par
exemple, le tableau d'intervieweur montré ici. Vous déclarez le tableau comme celui-ci, Andi, vous accédez à des éléments comme celui-ci. Le deuxième type est une disposition multidimensionnelle. Par exemple, un tableau à deux dimensions est déclaré comme ceci, et vous accédez à des éléments comme celui-ci. Le troisième type est un tableau de veste. Il s'agit simplement d'un tableau hors d'une course. Vous déclarez une veste, un béret comme celui-ci, puis créez une nouvelle instance de tableau pour chaque élément de niveau supérieur comme celui-ci , puis vous accédez à et des éléments comme celui-ci. C' est ce qu'on appelle un tableau de veste, car chaque élément de niveau supérieur peut avoir un nombre de différence hors des éléments de sous-niveau. Un tableau de deux par deux peut avoir un jack à droite comme ça. Alors, comment ces tableaux se comparent-ils ? Allons à Xamarin Studio et découvrons que j'ai écrit un programme qui initialise est trois. Soulevez chacun contenant un million d'éléments. Voici le tableau unidimensionnel, avec 1000 fois 1000 éléments sur cette boucle signes de valeur à chaque élément. Et voici le tableau à deux dimensions, maintenant initialisé aux milliers par 1000 éléments. J' ai deux boucles imbriquées pour passer par chaque ligne et colonne, et j'attribue une valeur à chaque élément du tableau. Le troisième message utilise le tableau vestes. L' inconvénient des tableaux de veste est que vous devez initialiser l'ensemble du tableau externe avec des instances de tableau. Ce code est là. Voyons quel tableau est le plus rapide. Prêt. On y va. Ce n'était probablement pas une surprise. Le tableau unidimensionnel est le plus rapide, avec 12 millisecondes. Vient ensuite le tableau de veste avec 16 millisecondes et enfin le tableau bidimensionnel avec 24 millisecondes. Maintenant, vous vous souviendrez probablement de la conférence précédente que le langage intermédiaire a un support
natif pour l'effacement d'une dimension C'est pourquoi le tableau à une dimension est le plus rapide sur le tableau déchiqueté vient en deuxième. Un tableau déchiqueté est simplement un tableau unidimensionnel imbriqué dans un autre
tableau unidimensionnel . Donc, vous obtenez toujours les avantages de vitesse de la prise en charge du langage intermédiaire intégré. Peut-être étonnamment, le tableau à deux dimensions est le plus lent, et c'est parce qu'un tableau à deux dimensions s'est livré. Net est juste une classe sans aucun support d'exécution spécial. Laisse-moi te montrer mes compilations. Froid. Si je définit un point d'arrêt, relancez le programme sur le commutateur pour démonter la vue ons. Cherchez la bonne ligne ici. Voici le tableau unidimensionnel. Vous pouvez voir l'instruction d'éléments familiers qui écrit la valeur dans l'élément Vous êtes un élément. Et voici le jittery JAG d'abord une instruction low elements aux charges, la référence au tableau interne, puis une instruction set elements pour droits une valeur dans ce tableau interne. Mais maintenant, regardez le tableau à deux dimensions. C' est simplement un appel à un ensemble statique. Les méthodes de votre méthode A Class one appellent pour chaque accès à l'élément, plus quelle que soit l'implémentation est à l'intérieur de
cette méthode set, c'est beaucoup plus de codes pour les exécuter. Les tableaux unidimensionnels et déchiquetés. Maintenant, vous venez de voir qu'un tableau à une dimension est plus rapide qu'un tableau à deux dimensions, donc nous pouvons accélérer un tableau à deux dimensions en utilisant une technique appelée aplatissement des tableaux. Jetez un oeil à ces trois par trois entiers déjà hors. C' est un total de neuf éléments que je peux exposer comme ça. J' ai codé par couleur chaque rangée d'éléments. Maintenant, je peux également emballer toutes ces données dans un tableau unidimensionnel comme celui-ci, je peux accéder et des éléments étant donné la ligne et la colonne comme ceci. Alors essayons-le dans le code. J' ai un programme ici qui met en place un tableau d'entiers 1000 par 1000, puis perdent à travers tous
les éléments qui y accèdent un par un. Voici la même couche, mais avec un tableau unidimensionnel avec un million d'éléments. J' ai encore les deux boucles imbriquées pour accéder à All rose dans toutes les colonnes, mais maintenant j'ai utilisé la formule de traduction pour aplatir la ligne et la colonne dans un
index unidimensionnel . Quels manteaux pensez-vous que c'est plus rapide ? C' est dur à dire. En fait, nous savons que le tableau unidimensionnel sera plus rapide. Mais maintenant, nous avons aussi les frais généraux hors de faire la multiplication supplémentaire pour traduire la ligne et la colonne en un index aplati. La multiplication supplémentaire compensera-t-elle les frais généraux du tableau à deux dimensions ? Allons le découvrir. Je dirige le programme maintenant. J' étais là, nous sommes. Le tableau unidimensionnel est toujours l'option la plus rapide, avec 15 millisecondes par rapport au tableau bidimensionnel. Avec 22 millisecondes, la matrice aplatie est 1,5 fois plus rapide. Voici un graphique avec tous les résultats de performance. Le tableau à deux dimensions est deux fois plus lent que le tableau à une dimension. Même dans le test d'aplatissement, lorsque le tableau unidimensionnel avait les frais généraux supplémentaires à faire une multiplication, il est encore 1,5 fois plus lent. Cela suggère que l'aplatissement des tableaux à deux dimensions est une bonne idée. Peut-être étonnamment, le tableau Jagged a
aussi de bonnes performances . Il est seulement 1,3 fois plus lent que le tableau à une dimension lorsque comparé aux résultats d'
aplatissement, il y a presque 15 millisecondes pour les aplatis un tableau dimensionnel sur 16 millisecondes. Pour le tableau déchiqueté, la différence de performance est seulement d'une millisecondes, ce qui est de 6%. Alors, comment devriez-vous utiliser une course si vous n'avez qu'une dimension hors jour za, utiliser une dimension effacée pour la meilleure performance. Si vous avez deux dimensions de données ou plus, envisagez d'aplatir le tableau. Si cela n'est pas possible, envisagez d'utiliser un tableau de veste. S' il n'y a pas d'autre option, utilisez un tableau multidimensionnel.
14. Astuce #5 : Ne Pas jeter des exceptions: dans cette conférence. Je veux jeter un oeil à l'utilisation responsable des exceptions. Alors, quelles sont les exceptions ? Le cadre Net utilise des exceptions pour gérer les erreurs. C' est ainsi que cela fonctionne quand quelque chose ne va pas dans un bloc de codes que les codes peuvent lancer une exception. En utilisant les instructions throw, les méthodes abandonnent immédiatement sur la pile est déroulée, ce qui signifie que le framework continue d'exécuter des instructions de retour implicites, sautant des appels de méthode imbriqués en allant de plus en plus haut de la pile d'appels jusqu'à ce qu'il atteint manteau avec un bloc try catch. Le framework saute dans le premier bloc catch qui correspond à l'exception du trône et commence à exécuter le code Là. Vous pouvez capturer l'exception en mettant un paramètre dans l'expression catch, sorte que cela sonne comme beaucoup de frais généraux, n'est-ce pas ? Quelle taille pensez-vous que la surcharge de performance sera pour lancer et attraper une exception ? Eh bien, découvrons. J' ai écrit un programme qui répète une opération très simple dans ce cas, incrémentant sur un entier un million de fois. Voici les premières méthodes de mesure qui incrémente simplement et la variable entière que j'étais. Voici la deuxième méthode de mesure, les mêmes codes, mais après incrémentation de la variable, le code lance en cas d'accession d'opération non valide. Maintenant, les instructions throw sont à l'intérieur d'un look try catch, il n'y a
donc pas besoin que le framework dot net commence à dérouler la pile à la recherche d'un bloc
try catch. Nous sommes déjà à l'intérieur d'un livre try catch, donc l'exécution saute directement à l'instruction catch, qui dans ce cas, ne fait absolument rien. Donc, ce code mesurera les frais généraux d'un seul lancer et attraper. Il n'y a pas de surcharge supplémentaire car la pile doit être déroulée. y a pas non plus de frais généraux parce que les codes de gestion des exceptions hors, car dans ce cas, le bloc catch est anti ready. On y va. Je dirige le programme. Vous attendiez à ce que les frais généraux de lancer une exception soit une incrémentation massive une variable
entière 1 000 000 de fois ne prend que six millisecondes, mais lancer une exception deux millions de fois prend 6,9 secondes supplémentaires, pas seulement secondes secondes. Le code, avec gestion des exceptions, est un stupéfiant 1150 fois plus lent. Cela conduit à l'emporter principale de cette section. N' utilisez jamais d'exceptions dans les codes critiques de mission. Assez juste. Je vais simplement éviter d'utiliser les instructions de lancer dans mon manteau, et nous sommes tous prêts ? Eh bien, non. Je dois également m'assurer que les bibliothèques sur les classes de base que j'utilise ne lancent pas exceptions. La réalité est que de nombreuses méthodes de classe utilisaient des exceptions pour signaler une condition non critique à l'appelant. Si je veux éviter ces exceptions, je ne peux pas appeler ces méthodes et directement. Mais je vais devoir vérifier mon include ater minutieusement pour les erreurs. Regardons le cas d'utilisation simple. Je vais exécuter le programme qui convertit les chaînes à introduire. Jetez un oeil à ce code. J' ai un message ici appelé Préparer la liste. Il utilise un constructeur de chaînes pour assembler un nombre à cinq chiffres en utilisant un générateur de nombres aléatoires. Lorsque la chaîne est prête, elle est stockée dans cette liste générique. Le code est répété 1 000 000 fois, donc finalement j'aurai 1 000 000 chaînes à analyser. Les méthodes de mesure sont ici. Le 1er 1 boucle à travers toutes les 1 000 000 chaînes sur les utilisations. Méthode de partie entière pour convertir le flux toe un nombre. Maintenant, si quelque chose se passe mal pendant l'analyse, les méthodes de pièces lancent une ancienne exception. J' attrape l'exception ici en supprimant simplement l'erreur. Les mesures secondes, les méthodes font la même chose. Boucle à travers toutes les chaînes de Parsons une par une. Mais maintenant, j'utilise l'essai. Les méthodes d'analyse au lieu de l'analyse n'est pas essayer. Les parties essaieront d'analyser le flux sur ne rien faire. Lorsque l'analyse échoue, il ne lancera jamais d'exception, donc je n'ai pas besoin d'essayer catch book. Ici. Vous pouvez voir la différence dans les discours sur le péché. Try Pars renvoie la valeur booléenne indiquant si le parson a réussi, il a également un paramètre de sortie pour stocker le résultat. Comparez cela aux méthodes d'analyse, qui renvoie simplement les résultats de passage directement et n'a aucun paramètre ou argument pour indiquer si l'analyse a réussi ou non. Ok, revenons aux méthodes de la liste de préparation. Jetez un oeil à la côte. Voyez-vous quelque chose d'étrange à la fin de mon tableau de personnages ? J' ai une lettre supplémentaire X. Le générateur de nombres aléatoires génère ici un nombre entre zéro et 10 donc 10% de réduction le temps qu'il ajoutera la lettre X au flux au lieu d'un chiffre valide. Si je mets un point de rupture juste au-delà de la méthode, appelez exécuter le programme, puis inspectez le contenu de la liste générique. Ensuite, vous voyez que quelques nombres sont invalides car ils contiennent sur X au lieu d'un chiffre. Les méthodes d'analyse et de test des pièces échouent sur ces numéros uniquement sur les pièces. Les méthodes lanceront une exception lorsque cela se produit. Donc, en se basant sur ce que nous savons déjà sur les exceptions en ce sens qu'elles sont vraiment lentes, nous nous attendons à ce que les méthodes try parse fonctionnent beaucoup mieux que la méthode parts. Tu crois que la différence sera grande ? Allons le découvrir. Je vais exécuter le programme, une mesure de la différence de performance. On y va. Et voici les résultats. Les méthodes Tri Parse prennent 618 millisecondes à Rome. Les méthodes de pièces prennent 3727 millisecondes, donc l'analyse est six fois plus lente que les pars try en raison de la gestion supplémentaire des exceptions. Nous avons donc vu que les conversions de temps de données peuvent également être problématiques. Manteau critique de la mission. En raison du fait que de nombreuses méthodes de conversion lancent des exceptions lorsque les données d'entrée
sont invalides, invalides,existe-t-il d'autres situations courantes où des exceptions sont lancées ? Oui, c'est que je vais t'en montrer une de plus. Voici donc un autre programme, et dans ce programme, je fais le contraire. Sur le plus gros programme. Je convertis des entiers en arrière deux chaînes. J' ai un message de liste préparé ici. Gardes. Maintenant, il remplit un individualiste générique avec un million d'entiers aléatoires. Andi Down est ici les premières méthodes de mesure, qui boucle sur chaque entier dans la liste, et pour chaque nombre, il utilise une table de recherche pour convertir ce nombre en chaîne. Si vous cherchez ici, vous verrez que j'ai pré-défini sur inter votre dictionnaire de chaînes pour agir comme rechercher table en indexant la table de recherche avec un entier. Je peux rapidement trouver la chaîne qui correspond à cette interview. Revenons donc aux codes de mesure. J' utilise la table de recherche pour trouver la chaîne et la stocker dans la variable s Si quelque chose se passe mal, ces instructions catch vont attraper l'exception en supprimant la flèche. Jetons maintenant un coup d'oeil aux deuxièmes méthodes de mesure. Il fait exactement la même chose, mais il y a un chèque supplémentaire ici. Je teste si l'entier est réellement dans le dictionnaire avant de l'utiliser pour rechercher le flux
correspondant. Ceci, sauf la vérification évite une exception. Et donc je n'ai pas besoin d'essayer. Bloc d'argent. Ok, revenez au message de la liste de préparation. Voyez-vous le validateur in ? C' est très simple. J' ai 10 entrées dans le dictionnaire pour les chiffres de 0 à 9, mais le générateur de nombres aléatoires génère des nombres entre zéro sur 10. Donc, dans 10% de rabais sur les cas, j'aurai le numéro 10 dans la liste sur la table de recherche n'a pas d'entrée pour ce numéro. Si je mets le point de rupture juste au-delà du médical, exécutez le programme et puis inspectez le contenu de la liste générique, alors vous pouvez voir que parfois j'ai un 10 dans la liste. La recherche du dictionnaire échouera pour ce nombre et seulement la mesure. Une méthode lancera une exception lorsque cela se produit. Donc, en se basant sur ce que nous savons sur les exceptions relatives aux frais généraux de performance, nous nous attendons à ce que la mesure A soit plus lente que la mesure B. Nous allons
donc vérifier que je vais exécuter le programme et mesurer la différence de performance . Ici, nous allons et voici la mesure des résultats a prend 1180 millisecondes pour exécuter la mesure
ne prend que 167 millisecondes, donc la mesure A est sept fois plus lente que la mesure soit en raison de la gestion des exceptions supplémentaires . Voici tous les résultats de mesure Combine en une seule référence ici sont des parties et essayez pars avec la méthode parts prenant 3727 millisecondes sur les pars try ne nécessitant que 618 millisecondes car il n'a pas besoin de lancer une exception si les données d'importation sont invalide, il s'
agit d'une baisse du temps d'exécution de plus de 80%. Ensuite, le test des secondes avec les recherches du dictionnaire effectuant une recherche sur la gestion des exceptions prend 1180 millisecondes. Mais si nous vérifions d'abord la clé, puis effectuons la recherche, nous évitons toutes les exceptions ensemble. Sur la course, temps passe à 167 millisecondes, une baisse du temps d'exécution de plus de 80 %. Alors, quelles sont les meilleures pratiques pour utiliser les exceptions ? Eh bien, si vous utilisez des exceptions dans une boucle critique de mission, alors seulement les utiliser pour indiquer une condition fatale qui nécessite que vous soyez à bord de la boucle entièrement. N' utilisez pas d'exceptions pour les conditions non fatales que vous gérez en passant simplement à l' itération de boucle
suivante. Ne mettez pas. Essayez des blocs de capture dans un code profondément imbriqué ou dans des fonctions AP I de bas niveau car ils vont ralentir vos manteaux. Mettez la gestion des exceptions aussi près que possible du programme principal. N' utilisez jamais de capture générique. Instructions d'exception, qui met en cache toutes les exceptions car il interceptera également des conditions non fatales sur. Il ne sera pas immédiatement évident pourquoi votre code est lent Si vous écrivez un A P. Je n'utilise pas d'exceptions pour des conditions limites non critiques telles que la conversion de données invalides ou échec d'une opération de recherche. Considérez le modèle d'analyse d'essai pour cela avec une valeur de retour booléenne indiquant le succès et un paramètre de sortie pour renvoyer des données. Quoi que vous fassiez, n'utilisez jamais d'exceptions pour contrôler le flux de votre programme.
15. Astuce #6 : Utilisez pour au lieu de faire un service de foreach: cette conférence sera axée sur l'optimisation des boucles. Un conseil commun que vous entendez souvent sur l'accélération des boucles est que vous êtes censé utiliser quatre instructions au lieu de pour que chacune regarde la collection. Mais est-ce vrai, Et si oui, quelle est la différence de performance entre les deux ? Commençons par regarder la mécanique de Z quatre et pour chaque déclaration, je vais commencer par quatre. Lorsque vous utilisez une boucle for pour accéder aux éléments d'une collection, nous commençons généralement par une boucle qui compte de zéro au nombre d'éléments de la collection Minds one. Ensuite, nous utilisons sur l'index de l'expression pour accéder à chaque élément. À son tour, un quatre Luke n'est possible que si vous avez un accès direct à tous les éléments de la collection, généralement via les crochets. Dans la méditation supplémentaire en C pointu, le tableau répertorie la liste générique sur les classes de tableau. Tous supportent l'indexation. Une autre façon d'accéder aux éléments d'une collection est d'utiliser dans le numérateur dans les
coulisses . Le pour chaque instruction commence par la création d'un nouvel objet numérateur dans d'autres rasoirs ont seulement trois membres. Champ actif qui contient la valeur de l'élément de collection en cours un message de
déplacement suivant pour passer à l'élément suivant. Andi réinitialise les méthodes pour revenir au début de la collection. Par conséquent, chaque instruction met en place une boucle qui continue d'appeler move next jusqu'à ce qu'elle atteigne la fin de la collection. Au cours de chaque itération de boucle, le champ courant contient à la valeur des éléments en cours. Alors quelle déclaration est plus rapide ? C' est dur à dire. En fait, les quatre hommes d'État ont besoin d'un indice, donc sur le coût,
être en mesure d'accéder directement à n'importe quel élément peut être élevé. Mais sur les autres mains, Ford chacun doit mettre en place sur l'objet aérateur inem sur, puis appeler le mouvement prochain message dans chaque boucle, il oration. Donc, tout dépend de ce qui est plus rapide, l'indexeur ou le mouvement. Méthodes suivantes Voici les avantages et les inconvénients de chaque méthode. L' instruction quatre est potentiellement la plus rapide en raison de sa simplicité, mais elle nécessite une collection avec un indexeur. Sur les collections ne savent pas à l'avance, dans quel ordre les éléments seront accessibles à travers l'indexeur, sorte que toutes les valeurs devront être chargées en mémoire d'abord, pour chacun est plus complexe car il utilise un énorme cratère dans les coulisses, et il nécessite un appel aux méthodes de déplacement suivantes pour passer à l'élément suivant. Mais l'avantage en fonctionnement est qu'il fonctionne sur n'importe quelle collection. Un autre avantage est que la valeur actuelle est calculée à la demande, sorte que toute la collection n'a pas besoin d'être en mémoire pour être inaugurée. Conseils sur Internet environ quatre et pour chacun est mélangé, certains disent toujours utilisé pour. Mais d'autres disent que les améliorations de performance ne valent pas la peine. Laissez-moi vous montrer un programme qui mesure la performance des deux déclarations pour une variété hors collections. J' ai écrit le programme qui met en place trois collections sur 10 millions d'entiers une
liste de tableaux , une interview générique publiée sur un tableau d'entiers réguliers. Les listes sont initialisées dans cette méthode. Ici. Préparer des listes. Le message génère 10 millions de numéros aléatoires entre zéro et 255 sur les annonces. Ils orent les trois listes, puis viennent à des méthodes de mesure. Ici, il y en a six. Je vais commencer par la 1ère mesure. Un est lâche à travers les 10 millions d'éléments de la liste des tableaux avec une boucle simple pour et utilise l'indexeur intégré de la classe Iraniens pour accéder aux éléments. Maintenant, le message suivant mesure A à parcourir également tous les éléments de la liste du tableau. Mais maintenant, en utilisant un pour chaque instruction au lieu des quatre instructions, donc dans les coulisses pour chacun va créer sur dans les objets numérateur qui marche séquentiellement à travers chaque élément de la collection. Les deux méthodes suivantes sont la mesure Être une tante Mesure B deux. Ils font exactement la même chose, mais avec le générique enterré votre liste au lieu de la liste du tableau. Et enfin, j'ai les méthodes Mesure C 1 et Mesure C 2. Encore une fois, ils ont regardé à travers les 10 millions d'entiers, mais maintenant ils utilisent le tableau entier. Donc, vous voyez les trois classes de collection supporte à la fois l'indexation sur dans l'admiration, ce qui sera le plus rapide. Voyons que je suis en cours d'exécution du programme maintenant, alors attendons les résultats. Et voici les résultats. L' utilisation de quatre tableaux tombés prend 98e millisecondes en utilisant pour chaque tableau Alan prend des centaines et trois millisecondes, une petite différence. Mais l'utilisation de quatre et pour chacun sur la liste génétique, prend respectivement 300 huit sur 501 millisecondes, et nous obtenons la plus grande différence de performance lors de l'utilisation de quatre et pour chacun sur une
liste de tableaux , respectivement. 302 sur 872 millisecondes. Tu vois ça pour une augmentation ? La différence est très faible sur l'optimisation A pour chaque deux ou quatre ne vaut probablement pas l' effort. Mais pour la liste générique et la liste des tableaux, la différence est assez grande. Pourquoi c'est ça ? Jetons un coup d'oeil au froid intermédiaire. Je vais commencer par la méthode Measure C une qui utilise une boucle simple pour accéder à chaque élément dans un tableau entier. Voici donc la boucle for. Ces trois instructions de langue intermédiaire définissent la variable de 20 Ensuite, nous sauterons à l'
emplacement un F sur pour comparer la variable I à la valeur de 10 millions. Si j'ai moins de 10 millions, nous continuons ici sur Exécuter le corps de la boucle. Vous pouvez voir l'instruction familière de l'élément de charge ici pour accéder aux éléments du tableau. Enfin, les codes continuent ici, où nous ne sommes pas un à la variable par vérification à nouveau. Si elle est inférieure à 10 millions maintenant, regardons les mêmes couches dans la méthode Measure C 2. C' est presque le même arrêt sur la variable d'index à zéro, sautant à l'emplacement 24 comparant l'index à la longueur du tableau. Andi continue si c'est moins. Mais regardez ce morceau ici juste avant d'exécuter le nouveau corps. Nous avons quatre instructions. Emplacement de chargement pour l'emplacement de chargement. Trois éléments de charge sur l'emplacement du magasin. Un. Ces quatre instructions. Récupérer les éléments du tableau actuel et le stocker dans l'emplacement un, ce qui correspond à la variable I. Donc, la différence entre un quatre et A pour chaque instruction pour les tableaux réguliers n'est que ces quatre langues intermédiaires les instructions à ce sujet sont la raison pour laquelle la différence de rendement des mesures
entre les deux est si faible. Le compilateur ne crée pas réellement sur dans les objets numérateur en utilisant des courants et se déplace ensuite. Au lieu de
cela, il émet un look quatre légèrement modifié pour implémenter l'énumération. Maintenant, regardons les listes génétiques. Dans la mesure. Soyez un sur la mesure B deux méthodes. La mesure, soit l'une des méthodes, est très simple. Tout d'abord, nous obtenons l'ensemble familier des instructions pour la boucle quatre ici. Ensuite, lors de l'accès aux éléments de liste, les codes utilisent un appel virtuel à un indexeur. Les appels obtiennent l'objet. La valeur renvoyée est un entier qui est stocké à l'emplacement vers. Mais la mesure B 2 est très différente. Pour chaque instruction commence par appeler les méthodes get in numérator sur les objets de liste . La liste dans numérateur est en fait une structure et non une classe sur. Il est stocké dans l'emplacement des codes, puis appelle. Déplacez ensuite sur l'opérateur in et continue si le résultat est vrai. Ensuite, il obtient la valeur des éléments actuels en appelant la propriété, obtenir les courants et stocke les résultats dans l'emplacement un. Ensuite, le corps de la boucle est exécuté, et puis nous sommes de retour à la prochaine étape. Cette couche est beaucoup plus complexe que d'accéder simplement aux éléments de liste à travers l'index. Amélioré, il fonctionne plus lentement. Maintenant, regardons la liste des tableaux dans les méthodes. Mesurer une mesure de 1£ A. Pour il n'y a rien de spécial dans la mesure
A, une boucle régulière pour sur un appel virtuel à l'indexeur d'élément obtient. Mais regardez ce morceau ici. L' injection get item retourne et objets, pas un entier. Donc, avant de pouvoir stocker les résultats, il doit être déboisé avec cette instruction d'unbox ici, et nous avons déjà appris que la boxe et le unboxing introduisent une
surcharge de performance significative sur votre côte. Maintenant, ici est mesurée. A de ce manteau ressemble beaucoup au culte de la liste génétique de la mesure B 2. Mais regardez les différences ici d'abord. La liste de tableau dans numérateur est une classe sur non attachée, ce qui signifie que le cool pour obtenir courant est un appel virtuel au lieu d'un appel régulier. Cela peut sembler un détail trivial, mais l'instruction colvert est, en fait, plus
lente que l'instruction d'appel. Mais deuxièmement, obtenez les retours actuels avec des objets et non un entier. Donc, le résultat doit être déboisé à nouveau avant de pouvoir être stocké, ce qui se produit ici avec l'instruction Unbox. Donc, voici une importance. Enlevez pas de générique dans. Les opérateurs retournent toujours la valeur actuelle en tant qu'objet. Donc, si vous les utilisez toe dans les types de survaleur d'exploitation, ils vont déboiser en arrière-plan toujours utilisé énumérateurs génériques, si possible. Voici tous les résultats de mesure combinés dans un graphique utilisant £4 pour chacun sur un tableau prend, respectivement, 98 sous des centaines et trois millisecondes en utilisant quatre et pour chacun sur une liste génétique prend respectivement 308 et 501 millisecondes sur l'utilisation de quatre et pour chacun sur une liste de tableaux prend ,
respectivement, 302 sur 872 millisecondes. Nous avons vu qu'il y a très peu de différence entre un quatre sous pour chacun lors de l' exploitation d'un tableau régulier. La raison en est que le compilateur optimise le tableau et l'admiration en émettant une boucle quatre
légèrement modifiée. Au lieu de passer par les tracas, créant sur les objets de l'opérateur in avec des listes génériques, nous voyons une nette différence entre quatre. Et pour chacun, le numérateur est instruct, qui est un type de valeur, et donc il peut stocker des types de valeur comme des entiers efficacement et pourtant dans l'admiration, est toujours 1,6 fois plus lent que l'utilisation d'une boucle for. Donc ici, il fait parfait sens orteil optimiser un pour chaque boucle en réécrivant son dans une
boucle for . Et enfin, regardant les listes de rayons, nous avons vu qu'il y a beaucoup de déboxe en coulisses. L' indexeur renvoie. Un objet qui doit être converti en un entier sur l'opérateur in est une classe et ne pas détruire sur. Il retourne la valeur actuelle en tant qu'objet, qui doit à nouveau être converti en un entier en fonctionnement sur une liste de tableau a une énorme pénalité . Il est deux points huit fois plus lent que l'utilisation d'une boucle for. C' est ainsi que vous choisissez entre quatre et pour chacun. Si vous utilisez un tableau, ne vous embêtez pas à réécrire un pour chacun en un quatre. La différence de performance n'en vaut tout simplement pas la peine. Lors de l'utilisation d'une liste générique, l'utilisation d'une quatre instructions est 1,6 fois plus rapide que l'utilisation d'un pour chaque instruction, donc réécrire pour chacune dans une boucle for en vaut vraiment la peine. Et pour les listes de tableaux, les améliorations sont encore plus grandes. Une instruction quatre sur une liste de tableaux est 2,8 fois plus rapide. Mais si vous pensez à tout le boxe et le unboxing en arrière-plan, vous pouvez en fait envisager de réécrire la liste du tableau en utilisant une liste générique place. Et enfin, si vous utilisez pour chacun et que vous êtes dans l'exploitation d'une collection hors temps de valeur, assurez-vous
toujours que vous utilisez le générique Anoma Reiter, qui est dans le E une interface de tisane nominale sur non le non générique dans numérateur, qui est dans l'interface E innombrable sur. La raison en est que l'opérateur non générique dans retournera toujours la
valeur actuelle en tant qu'objet, sorte que votre couche compilée contiendra beaucoup d'instructions de boîte et de déballer et en utilisant un générique dans les mémoires qui évitera cela.
16. Comment le collecteur de déchets fonctionne-t-il ?: cette conférence est par des demandes off sucer. C' est marrant qui m'a demandé de regarder dans la collecte des ordures ? Merci pour la demande, meuniers. J' espère que vous apprécierez cette conférence, et j'espère aussi que je prononce votre nom correctement. Si quelqu'un d'autre a des demandes spéciales sur le sujet que vous aimeriez que j'aborde, envoyez-moi un message et je le travaillerai dans ma feuille de route ou dans les leçons à venir. Si vous vous souvenez dans la deuxième conférence de ce cours, celle sur
la mémoire de tas dans la section de base,
nous avons vu ce qui se passe lorsque les variables de type référence sortent de la portée. Les variables qui existent sur la pile sont détruites sur les objets correspondants sur le tas. objets référencés R D D continuent d'exister et ne sont pas détruits immédiatement. J' ai brièvement mentionné qu'un processus distinct appelé garbage collector
nettoie périodiquement ces objets. Donc, dans cette conférence, nous allons regarder de plus près le collecteur de déchets. Qu' est-ce que le garbage collector et comment fonctionne-t-il ? Commençons par un programme très simple. Ce programme n'a qu'un message avec un tableau d'objets variables locaux. Le tableau est initialisé avec cinq objets sur ces cinq objets résident également sur le tas adjacent au tableau lui-même. Maintenant, pour rendre les choses plus intéressantes, supprimons les éléments de tableau deux et trois en étant assis. Il des éléments de tableau pour connaître les objets correspondants. numéros deux et trois existent encore sur la chaleur, mais maintenant ils sont déréférencés. n'y a aucune référence à ces objets de n'importe où dans la couche. Que se passe-t-il quand le collecteur d'ordures frappe la porte ? Filet. Garbage Collector est un collecteur de marque et de balayage, qui quitte. Il y a deux étapes distinctes de la collecte des ordures d'une étape de marquage sur une étape de balayage au cours de la phase de marquage. Le garbage collector marque tous les objets de vie sur le donc dans cet exemple, ce serait le tableau lui-même et les objets 01 sur quatre. Les objets deux et trois sont ignorés car il n'y a aucune référence à ces objets de n'importe où dans la couche. Vient ensuite le stage de balayage tous les objets qui n'ont pas été marqués dans la référence R
D de l'étape précédente sur, et dans ce stade ils sont alloués à partir du tas. Donc, dans cet exemple, les objets deux et trois n'ont pas été marqués, et ils sont donc alloués sur. Cela laisse une sorte de trou sur le tas. Le collecteur à ordures de filet pour chien effectue une étape supplémentaire après le balayage, qui est appelé compacts. Dans l'étape compacte, tous les trous sur le tas sont supprimés, donc dans cet exemple, l'objet quatre est déplacé vers le haut pour remplir le trou Cet objet à laisser derrière la marque et balayer garbage collector est très bon pour localiser. Chaque objet référencé affaire sur le tas sur le supprimer. Mais il a aussi un gros inconvénient. C' est très lent. Au cours de l'étape de marquage, le garbage collector doit inspecter chaque objet en mémoire pour déterminer s'il s'agit de la vie ou du référencé. S' il y a plusieurs milliers d'objets sur le tas, votre programme va effectivement geler pendant un certain temps car le garbage collector inspecte chaque objet. Le processus est également très inefficace parce que les objets longs sur le tas sont vérifiés et revérifiés, supportant chaque cycle car ils pourraient être d référencés à tout moment. Donc, un objet très long peut être vérifié des centaines de fois s'il est encore vivant. La solution à ce problème est appelée un garbage collector générationnel, le dot net garbage collector est générationnel, et il a trois générations, que vous pouvez visualiser comme trois tas distincts. Toutes les nouvelles allocations vont dans le premier tas de génération appelé Generation Zero. Donc, si nous revisitons le programme de test avec le tableau de cinq éléments avec les éléments deux et trois, régler no. Ensuite, les mises en page de mémoire ressembleraient à ceci. Tout est le même qu'avant, mais maintenant tous les objets résident dans la génération zéro génération. L' un vient à notre anti. Le premier cycle de collecte effectue un marquage et un balayage sur tous les objets qui ont survécu au balayage se déplacent vers Génération 1. Donc, après un cycle, la disposition de la mémoire ressemble à ce tableau Z. Les objets Andi 01 et 4 ont survécu au balayage et sont maintenant dans la première génération. Maintenant, imaginez que le programme continue à ce stade. Il met un nouvel objet cinq dans les éléments du tableau à toutes les nouvelles allocations. Passez dans une génération zéro afin que les mises en page de mémoire ressemblent à ceci. Comme vous le voyez, c'est une situation intéressante. Le tableau récite dans la génération un, mais ses éléments sont dans les générations zéro et une. C' est parfaitement valable. Maintenant. Le garbage collector repart pour un deuxième cycle. Tous les objets de génération un se déplacent à génération sur le nouvel objet dans la génération zéro se déplace à la génération un si le programme continue et met un nouvel objet six dans un élément rare trois, il irait à nouveau dans la génération zéro. Nous avons maintenant un tableau de génération en génération qui se réfère aux objets de la génération 01 pour une nouvelle preuve de la violence. Vous vous demandez peut-être pourquoi tout cela se passe ? Pourquoi ces trois générations ? Eh bien, la réponse est très simple. générations aident à limiter le nombre d'objets dans la génération zéro. Chaque cycle de collecte efface complètement la génération zéro de tous les objets. Ainsi, dans le cycle suivant, le garbage collector n'a qu'à inspecter les nouveaux objets qui ont été créés après le dernier cycle. Bien sûr, le problème ne disparaît pas. Le garbage collector a simplement déplacé les objets ailleurs. Mais voici les générations clés que l'on recueille très rarement. Le garbage collector suppose que tout ce qui atteint la génération doit être un objet de
longue durée qui n'a pas besoin d'être vérifié très souvent, donc cela résout deux problèmes. abord, il réduit le nombre d'objets dans la génération zéro, sorte que le garbage collector a moins de travail à faire objets de
longue durée qui survivent dans la génération à je ne suis pas vérifié très souvent, ce qui est exactement ce que nous voulons. Le garbage collector générationnel est un bel algorithme de haute performance, mais il a un inconvénient important. Il est inefficace comme le traitement de grands objets à vie
longue. Considérez un grand et long objets sortant seront alloués dans la génération zéro. Il survit au premier cycle, et la chaleur est compactée, ce qui déplace potentiellement l'objet en mémoire. Ensuite, il passe à la première génération. Il est compacté sur les mouvements de génération à tous. En tout, ce sont deux compactages et deux mouvements, Donc un total de quatre opérations de copie de mémoire pour un seul objet avant qu'il arrive en génération sur le garbage collector l'ignore pendant un certain temps. Si l'objet est très volumineux, ces quatre opérations de copie par objet peuvent introduire des frais généraux de performance de signification. Donc, la solution à ce problème est d'avoir deux tas séparés, l'un pour les petits objets sur un. Pour les grands objets, le design ressemble à ceci. Filets d'intérieur qui sont trop hanches. Le petit objet. Lui, qui travaille avec les trois générations dont nous avons discuté précédemment, et la grande astuce de culture, la chose particulière à propos du grand tas d'objets est qu'il n'utilise pas de générations. En fait, il n'a qu'une seule génération, qui est synchronisée avec la génération à partir du petit objectif. Donc, lorsque la collecte des ordures était la génération de processus à partir du petit de votre équipe, il fonctionne également à travers l'ensemble grand objectif. Un autre fait intéressant sur le tas d'objets volumineux est qu'il ne compacte pas le pendant le cycle de balayage. Il fusionne simplement les blocs de mémoire libres ensemble, mais il ne fait aucun compactage pour optimiser la quantité totale d'espace libre. Vous vous demandez peut-être ce qui détermine si un objet est petit ou grand ? Le seuil de taille est de 85 kilo-octets. Tous les objets à 85 kilo-octets pour les plus grands vont directement au tas d'objets volumineux. Tous les objets plus petits que cette limite entrent dans le petit tas de projet. Avoir ces deux tas séparés résout le problème des
grands objets vivants. Ils n'ont plus besoin d'être copiés quatre fois avant de finir en génération vers, mais ils vont directement dans le tas d'objets volumineux, qui n'est traité que de génération à et jamais compacté. Et là, vous l'avez,
le collecteur d'ordures de filets pour chiens est une qualité d'ordures générationnelle ou qui utilise un cycle
compact de balayage de marque . Il a des tas séparés pour les grands objets et les petits objets. Si vous y pensez,
le garbage collector DOT Net fait quelques hypothèses très spécifiques sur les objets et les durées de vie. Tout d'abord, il suppose que les objets seront soit de courte durée, soit de longue durée. Tous les objets de levage court doivent être alloués, utilisés et jetés dans un seul cycle de collecte. Tout objet qui glisse à travers les fissures, pour
ainsi dire, est pris dans la première génération dans le cycle suivant. Donc, tout objet qui survit aux cycles de collecte finit par génération et doit être un objet vivant
depuis longtemps. En outre, tout objet de plus de 85 kilo-octets est toujours considéré comme un objet de longue durée. En regardant la fréquence de collecte sur les différentes générations, il est clair que le garbage collector suppose que l'écrasante majorité des objets sera de courte durée. Donc, je peux résumer mes conseils d'optimisation de la mémoire en une seule phrase. Ne pas aller à l'encontre de ces hypothèses. Alors qu'avons-nous appris ? Le garbage collector utilise un balayage de marque et un cycle compact. Le garbage collector a deux tas séparés pour les grands et les petits objets. Le grand tas d'objets sur le petit objectif. Le petit objet qu'il utilise trois générations, tous les nouveaux objets sont alloués dans la génération zéro et progressent vers la génération à l' objet
grand. Il a une seule génération qui est traitée avec la génération à partir du petit objectif. En outre, le tas d'objets volumineux ne compacte pas le tas. Pour optimiser la mémoire libre, le garbage collector fait deux hypothèses importantes sur la taille et la durée de vie des objets. Un 90% sur tous les objets de moins de 85 kilo-octets doivent être de courte durée de vie à tous les objets de
plus de 85 kilo-octets doit être longue durée.
17. Astuce #7 : Optimiser la collecte de déchets: bienvenue aux parties de la série de conférences sur la collecte rapide des ordures. Dans cette conférence, nous allons examiner plusieurs optimisation des performances est de faire
fonctionner le garbage collector aussi vite que possible. Mais d'abord, résumons ce que nous avons appris sur le collecteur de déchets. Dans la conférence précédente, le collecteur de déchets DOT filets utilise un balayage de marque sur cycle compact pour nettoyer les
objets référencés D de la chaleur. Il est utilisé est de séparer les tas pour les grands et les petits objets. Le grand ou la jetée sur ce petit objet, les petits objets qu'il utilise. Trois générations, tous les nouveaux objets sont alloués dans la génération zéro ons. Les progrès vers la génération zéro sont recueillis très fréquemment. Générations on mains trop moins. générations ont aidé à limiter le nombre d'objets dans la génération zéro. Chaque cycle de collecte efface complètement la génération zéro de tous les objets du cycle suivant , le garbage collector n'a qu'à inspecter les nouveaux objets qui ont été créés après le dernier cycle. La première optimisation des performances basée sur la mémoire que nous allons examiner est simplement limiter le nombre d'objets que nous créons dans la génération zéro. Moins d'objets sont créés, moins
le garbage collector doit faire pour nettoyer le tas. Il y a deux stratégies que vous pouvez suivre pour limiter le nombre d'objets sur la hanche. La première consiste à vous assurer que vos codes ne créent pas d'objets redondants n'importe où sur seconde pour allouer, utiliser et jeter vos objets le plus rapidement possible afin qu'ils soient déjà alloués par le garbage collector dans le cycle suivant. Si vous attendez trop longtemps entre l'allocation, en utilisant sur le rejet de vos objets, vous courez le risque de finir dans les générations une ou deux. Donc, pour les objets de courte durée, vous voulez que vos manteaux soient aussi serrés que possible. Jetons un coup d'oeil à quelques exemples. Voici un fragment de code qui boucle 10 000 fois sur construit une chaîne, en utilisant un constructeur de chaînes avec un froid à la méthode appends. Tu vois le problème avec ce manteau ? Je te donne 10 secondes pour réfléchir. Voici la solution. Le problème est avec la concaténation de chaîne à l'intérieur du message ajoute, vous vous souviendrez que les chaînes sont immuables, et donc le message de deux chaînes sur l'addition crée des objets de chaîne supplémentaires sur le tas pour chaque boucle qu'il oration le froid en bas évite ce problème par assemblage, appelant upend deux fois. La différence est 40 000 objets de chaîne de moins sur le tas. C' est une grande amélioration. Voici donc un autre exemple. Voyez si vous pouvez repérer le problème. Je te donne encore 10 secondes. Et voici la solution. Si vous stockez des entiers dans une liste de tableaux, les entiers sont encadrés sur la hanche. Les listes génériques évite ce problème en utilisant sur un tableau entier interne au lieu d'un tableau d'
objets, une simple modification des codes qui entraîne 20 000 en moins dans vos objets sur le ok. Encore un exemple. Un petit objet
statique est initialisé, puis beaucoup d'autres sectes s'exécutent en premier. Enfin, l'objet est réellement utilisé. Qu' est-ce qui ne va pas avec cette photo ? Je vous donne 10 secondes, et voici la réponse. L' objet est petit, mais l'écart entre l'allocation et l'utilisation est très grand, donc il y a une grande chance que les objets finissent dans la génération une ou deux avant qu'ils ne soient utilisés. Les codes en bas évite ce problème,
mon premier, mon premier, rendant les objets non statiques là, allouant c'est juste avant utilisation et enfin en définissant la référence de l'objet pour savoir juste après utilisation pour signaler à la poubelle qui ont été faits sur que les objets sont prêts à être collectés. Si vous n'aimez pas avoir aucune affectation sur tous vos codes, vous pouvez également envelopper les codes inférieurs dans le message, même que l'objet de référence est hors de portée lorsque vous quittez les méthodes. C' est ma solution préférée. La prochaine optimisation que vous pouvez effectuer consiste à affiner la durée de vie de vos objets. Le garbage collector suppose que c'est presque tout. Les petits objets seront de courte durée, et tous les grands objets seront longue levée, donc nous devrions éviter le contraire. Petit, long lifting, tous tex ou grands sujets sous peu. Il est instructif de voir ces combinaisons sur un graphique. Si je trace la durée de vie de l'objet horizontalement sur la taille de l'objet verticalement, j'obtiens les graphiques suivants. Les quadrants en bas à gauche et en haut à droite sont là où vous voulez être. Ces combinaisons hors objets, tailles et durée de vie correspondent exactement aux hypothèses du garbage collector. En haut à gauche en bas à droite ? quadrants sont en contradiction avec les hypothèses du garbage collector. Si votre code contient beaucoup d'objets provenant des quadrants, vous travaillez efficacement contre les hypothèses du garbage collector à la performance de votre manteau en souffrira probablement à la suite de cela. Alors, que pouvons-nous faire pour entrer dans les quadrants corrects ? Commençons par des objets. Durée de vie à re facteur grand, objets de levage
courts, nous avons besoin d'augmenter l'objet. À vie. Il existe une stratégie très simple pour cela, qui est appelée pool d'objets. L' idée est que, au lieu de rejeter souvent et les objets et d'allouer et de nouveaux objets. Au lieu de cela, vous réutilisez les objets existants. Parce que les mêmes objets sont utilisés encore et encore, il devient effectivement un objet de longue vie. C' est une stratégie très populaire pour optimiser le tas d'objets volumineux. Alors regardons un exemple. Voici un fragment de codes qui alloue une grande liste de tableaux sur, puis l'utilise deux fois, en rejetant lors de la réaffectation de la liste entre les utilisations. Comment pourriez-vous améliorer ce manteau ? Je vais vous donner 10 secondes pour y réfléchir, et voici la solution. Au lieu de cela, hors de jeter lors de la réaffectation de la liste, vous l'essuyez à la place avec un appel au message clair, puis réutilisez la liste pour la deuxième méthode, appelez la nouvelle couche. Les mêmes objets de liste de tableau sont utilisés encore et encore, sa durée de vie est augmentée sur les objets devient effectivement longue vie. Cette modification améliore les performances et réduit les chances que le tas d'objets volumineux devienne fragmenté. Maintenant, regardons le problème inverse. Nous avons un petit, longs objets de levage que nous devons réfractionner ou dans un des objets de courte durée. Comment ça fonctionnerait ? Voici un exemple. Cette couche remplit une liste de tableaux avec 10 000 paires d'objets. Chaque cheveu contient deux à l'intérieur du vôtre. Alors, qu'est-ce qui ne va pas avec ce code ? Je te donne 10 secondes pour y réfléchir. Le problème est que la liste du tableau est un objet volumineux, donc elle passe au grand objet qui est supposé être longue vie. Mais la liste est remplie de minuscules objets, 10 000 sur eux. Tous ces objets vont jusqu'à ce que le petit tas d'objets dans la génération zéro, car la liste du tableau conserve un orteil de référence chaque Eisen. Tous ces parents ne seront jamais d référence, et ils finiront par passer dans une génération. Pour la solution est d'utiliser une autre stratégie réfectoire populaire à la place, hors d'avoir une liste avec deux entiers dans chaque élément de liste. Nous divisons la liste des pièces en deux inter generations distinctes. Comme un entier est un type de valeur, il sera stocké avec le tableau. Donc maintenant, nous avons seulement deux plus grande augmentation dans le grand objet il et absolument rien dans la génération zéro. Problème résolu. La troisième optimisation que vous pouvez effectuer est de trouver Juin la taille de vos objets. Le garbage collector suppose que presque tous les objets lents seront de courte durée. Tous les grands objets auront une longue durée de vie. Donc, si nous avons les opposés dans nos codes, objets d'élévation longue
école ou les grands objets à gauche court, nous devons réfracteur la taille de ces objets pour les ramener dans la bonne charge. Conformité. Commençons par un grand objet court à gauche pour réduire la taille de cet objet. Il y a deux stratégies. Une division de l'objet des pièces dans des sous-objets, chacun de moins de 85 kilo-octets ou deux réduisait l'empreinte mémoire de l'objet. Voici un exemple de la deuxième stratégie. Une boucle remplit le tampon avec 32 milliers, mais c'est Pouvez-vous voir ce qui ne va pas avec ses codes ? Je te donne 10 secondes, et voici la réponse. La boucle remplit le tampon avec des piqûres, mais le tampon est défini comme un tableau à partir d'entiers. Le tampon contient 32 000 éléments sur Étant donné qu'un entier a une taille de quatre octets, cela ajoute jusqu'à 100 28 milliers de piqûres. Ceci est au-dessus du seuil de grand objet, et donc ce tampon va directement jusqu'à ce que le grand tas d'objets sur est collecté en génération à la solution est de refacturer le tampon comme il mord tampon. Maintenant, les emprises de mémoire hors du tampon sont exactement 32 000 octets, ce qui est plus petit que les grands seuils d'objets. Et donc il obtient des magasins sur le petit objectif dans la génération zéro, tout comme nous waas. Maintenant, regardons le problème inverse. Nous avons un petit, objet de
longue durée que nous devons re prendre en compte dans un grand, objet de
longues ascensions. Comment ça fonctionnerait ? La solution est, soit
de grossir l'empreinte mémoire de l'objet ou de fusionner plusieurs objets ensemble pour créer un objet plus grand qui peut aller sur le grand objectif. Voici donc le dernier exemple de cette conférence, cette couche déclare une liste de tableau statique sur. Ensuite, quelque part à mi-chemin, le programme commence à l'utiliser. Qu' est-ce qui ne va pas avec ce code ? Je te donne 10 secondes. Voici la réponse. Il est clair que l'objet est destiné à être un objet de longue vie car il est déclaré statique. Si nous savons aussi que la liste contiendra au moins 85 kilo-octets de jours, hein ? Ensuite, il est préférable d'initialiser la liste à cette taille. Cela garantit que la liste va directement sur le tas de projet volumineux car si vous n' initialisez pas la liste, elle obtient la capacité par défaut, qui en haut de ma tête est de 16 kilo-octets. Donc, la liste va dans le petit tas d'objets dans la génération zéro et finit par passer à génération à après potentiellement, avoir subi quatre opérations de copie de mémoire en initialisant la liste à la
bonne taille tout de suite, vous évitez la migration de la génération zéro à la génération entière. Il peut sembler étrange que vous puissiez optimiser la couche en agrandissant les objets, mais c'est exactement ce que nous faisons ici. Et parfois ça marche vraiment. Alors qu'avons-nous appris pour optimiser votre code de manière à ce que le garbage collector fonctionne aussi vite que possible ? Vous devez d'abord suivre ces stratégies, limiter le nombre d'objets que vous créez secondes alloue utiliser sur les rejets de petits objets aussi rapidement que possible. Seward réutilise tous les grands projets. Vous voulez travailler avec le garbage collector et non contre elle. Et vous devez donc vous assurer que tous les objets de vos codes sont petits et de courte durée. Quatre grandes et longues vies. Donc, si vous arrivez à avoir des objets qui sont soit luxuriante et court ascenseur ou petit sur longue portance , vous voudrez peut-être les prendre en compte pour les grands sujets sous peu. Vous pouvez soit augmenter la durée de vie de la guerre, diminuer la taille de l'objet et quatre petits objets de longue haleine. Vous pouvez soit diminuer le signe de vie, soit augmenter la taille. Tous ces changements seront bénéfiques pour la performance de votre manteau.
18. Astuce #8 : Utilisez les délégués rapides: dans cette conférence, je vais regarder de plus près. C' est les délégués. Maintenant, si vous n'êtes pas familier avec le concept des délégués, les délégués n'est rien de plus qu'un type qui enveloppe un mythique. Donc, où un type régulier définit quel type de données affaiblir, stocker, disons, diffuser dans vos dates, etcetera, un délégué définit quel type de méthode off nous pouvons appeler. Alors, quelle est la méthode pour les amateurs sur quel est le type de retour à définir ? Délégué dans C. Sharp, j'ai utilisé les mots clés délégués comme celui-ci. Cet exemple spécifique définit un appel de méthode qui s'attend à enterrer vos paramètres d'entrée un paramètre de sortie non sécurisé sur un type de retour vide. Maintenant, cette déclaration ne définit pas encore de variables. Tout ce que nous avons à ce stade est une nouvelle définition de type appelée ajoute Delegate, qui décrit une signature de méthodes particulières pour créer un nouveau délégué. Variable. J' ai besoin de le faire. Cela définit un nouveau type de variable off. Ce sont les délégués. Alors, comment puis-je signer la valeur à cette variable ? Eh bien, abord, nous devons avoir des méthodes quelque part dans notre code qui correspondent exactement à la signature que nous avons configurée plus tôt pour insérer vos paramètres d'entrée. Un entier génère un paramètre et un type de retour vide quelque chose comme ceci. Maintenant que j'ai l'implémentation des méthodes, je peux affecter la variable delicates comme ceci et ensuite je peux l'appeler comme ceci. Maintenant, ce que vous avez vu jusqu'à présent est appelé un délégués de distribution unique. C' est un délégué que j'ai initialisé avec une implémentation de message unique et quand j'
invoque les délégués, il appelle les méthodes uniques. Mais les délégués sont beaucoup plus puissants que cela. Je peux également mettre en place un délégué multi cast. Il s'agit d'un délégué qui peut appeler plus d'une méthode en une seule fois. Disons que je dois ajouter des méthodes dans mon manteau appelé Il est chaud et ajoute à. Je peux alors mettre en place les délégués comme ça. Notez que l'utilisation du plus est opérateur dans la troisième ligne. Ceci, comme une méthode supplémentaire à un délégué existant sur met effectivement en place un délégué multi cast. Tous les délégués peuvent être multi-interrogés en y ajoutant simplement plus de méthodes. n'y a pas de limite au nombre de méthodes que vous pouvez demander, mais s'il vous plaît ne pas aller à la mer à nouveau. J' invoque les délicats comme ceci sur cela appellera toutes les méthodes add dans l'ordre dans le même ordre que je les ai ajoutées aux délicats. Donc ça m'a fait me demander. Existe-t-il une différence de performance entre un casting unique et un multi-casting délégués ? Sur ? Quelle serait la pénalité en utilisant un délégué au lieu d'un appel de méthode régulier ? Allons le découvrir. Pour tester la performance des délégués, j'ai écrit le code suivant. Je commence ici par les délégués familiers qui ont ajouté deux chiffres ensemble. Voici deux implémentations hors mains méthodes qui sont complètement identiques et correspondent à la signature des délégués. Ensuite, j'ai fait trois tests dans mon premier test. J' appelle les méthodes publicitaires directement. Cela fournira une base de référence. Le deuxième test met en place un casting unique. Les délégués l'appellent deux fois. Cela nous dira ce que les frais généraux sont désactivés, en utilisant les délégués à la place d'appeler directement les méthodes publicitaires. Et enfin, j'ai envoyé un multi-délégués personnalisés, assigner avec deux méthodes, puis invoquer ce délicat. Une fois. Cela permettra de comparer la performance hors multi cast sur les délégués de casting uniques, et voici les résultats. Il y a une surcharge de performance de 9% lors de l'utilisation d'un délégué au lieu d'appeler
directement le gestionnaire , ce n'est pas trop mauvais, mais l'utilisation d'un délégué multi cast est plus de deux fois plus lent que d'appeler un unique en tant que délégués deux fois. Whoa. La raison en est due à la façon dont les produits délicats sont mis en œuvre. Dans les coulisses, un délégué est implémenté par la classe de délégués multi cast. Cette classe est optimisée pour les délégués unicast. Il utilise un message sur une propriété cible pour appeler directement une seule méthode. Mais pour les délégués multi cast, la classe utilise une liste d'invocation sur les listes génériques internes, contient des références à chaque méthode, et ils sont appelés dans l'ordre les frais généraux hors. Parcourez la liste d'invocation est ce qui cause cette grande différence de performance. Enfin, permettez-moi de vous donner un conseil. Parfois, je vois froid comme ça. Cette ligne de code définit une variable de démagogue sur, puis initialise. Est-ce avec une méthode DouBie qui ne fait rien. L' avantage de pré-initialiser la variable délicate est que nous n'avons plus à vérifier si la variable est non. Donc, au lieu de le faire, nous pouvons maintenant simplement le faire indépendamment. Si les délégués ont été initialisés, le code fonctionnera toujours. Les délégués appelleront le message factice sur ne rien faire ou appelleront les mess mannequins sur le message des signes
Duthie dans l'ordre. Mais s'il te plaît, ne fais pas ça. Vous avez vu dans les mesures de performance que les délégués de multidiffusion sont beaucoup plus lents qu' uniques en tant que délégués. Ainsi, la simple action off pré-initialisation de la variable déléguée rend la couche plus de deux fois plus lente. Voici donc mon conseil sur les délégués rapides en C. Sharp. Les frais généraux de performance à l'aide des délégués ne sont que de 9 %. Dans la plupart des cas, cela est parfaitement acceptable, et n'hésitez pas à utiliser des délégués n'importe où dans votre manteau où c'est pratique Breath si vous devez avoir les meilleures performances possibles, puis retirez tous les délégués de sections froides critiques et attraper ce 9% de réduction supplémentaire sur les performances. Et vous devriez toujours éviter d'utiliser des délégués multi cast dans Mission Critical Coast, car ils sont plus de deux fois plus lents, aussi uniques que les délégués.
19. Astuce #9 : Créer une usine de cours rapide: dans cette conférence, je vais voir si je peux accélérer un manteau très commun. Construire l'usine de classe. Maintenant, vous vous demandez peut-être, qu'est-ce qu'une usine de classe ? Autrement dit, une usine de classe est une classe spéciale qui construit d'autres classes à la demande en fonction d'une sorte de données de configuration externes. Vous voyez souvent des usines de classes utilisées lors de l'accès à des bases de données dans des codes. Nous voulons simplement accéder à la base de données sur. Nous ne nous soucions pas vraiment si les données sont stockées dans une base de données de serveur de suite Microsoft
sur la base de données Oracle ou Maya Scwe. C' est un détail de mise en œuvre dont les codes ne devraient pas avoir à s'inquiéter. Donc, dans les codes, vous pourriez voir quelque chose comme ça. Dans cet exemple, l'usine de connexion de classe est une usine de classe qui sait comment créer un objet de
connexion de base de données pour les données de vente. Donc, s'il y a un paramètre quelque part dans un fichier de configuration qui spécifie que toutes les
données de vente sont stockées dans une base de données d'article, alors la fabrique de classes peut utiliser ces informations pour renvoyer un point de données système. Les clients Oracle pointent la classe Oracle Connection. Vous pouvez voir comment les usines de classe, tout simplement cinq le froid énormément. Vous n'avez plus à vous soucier de l'endroit où certaines données sont stockées. L' usine de classe sait sur retournera automatiquement les objets de connexion corrects pour chaque requête. Les usines de classe ont un autre grand avantage. Ils nous permettent de déplacer les données à l'exécution si,
à un certain moment , les données de vente migrent vers une base de données my SQL, tout ce que nous avons à faire est de mettre à jour le fichier de configuration sur la prochaine demande de données, l'usine de classe lire les données de configuration modifiées et retourner A My SQL objets de connexion à la place Pour construire une usine de classe, vous devez terminer ce fragment hors couche. Donc, les entrées sont une chaîne qui contient le type de la classe à être. Au lieu de cela, elle a noté sur la sortie désirée est une instance réelle de
cette classe maintenant, c'est en fait un problème difficile à résoudre. Le plus proche que vous pouvez obtenir c'est compiler. Le temps est quelque chose comme ça. Cela fonctionne vraiment bien parce que le compilateur peut compiler n'importe quel scénario possible que nous pourrions rencontrer lors de l'exécution. Mais l'inconvénient est que nous devons anticiper toutes les utilisations possibles de l'
usine de classe à l'avance. Par exemple, il est complètement impossible de déplacer les données de vente vers une base de données My SQL si nous n'avons pas anticipé l'utilisation de mon SQL avec une instruction de cas correspondante avant les mains. Donc, ce manteau n'est pas très utile dans une usine de classe, mais il a les meilleures performances possibles. J' ajouterai le code à ma mesure plus tard pour servir de valeur de référence. Ok, donc une instruction de commutation ne suffira pas. Qu' avons-nous d'autre ? Une autre possibilité est d'utiliser la réflexion. Le système de réflexion adulte, l'espace de
nom a une très belle classe appelée activateur que nous pouvons utiliser pour construire n'importe quel type objets que nous aimons en fonction du nom de son type. Donc, je peux réécrire la méthode précédente pour utiliser la réflexion à la place, et cela ressemblera à ceci. Maintenant, cela va fonctionner parfaitement dans tous les scénarios, je peux jeter n'importe quel nom de type que j'aime. Si le type existe, la classe activator construira l'instance de classe correspondante. C' est parfait. Cependant, ce rhume a un gros problème. La réflexion est vraiment lente. Nous verrons comment lente dans un moment où je cours le benchmark, mais croyez-moi quand je dis que cela va introduire un revers de performance massif dans votre manteau, cela ne doit pas toujours être un problème. Nous pouvons supposer que les connexions aux bases de données ne seront pas créées si souvent. En outre, ouverture d'une connexion à une base de données est une opération beaucoup plus lente que la construction de la classe, donc un ralentissement de la construction va à peine être perceptible pour l'utilisateur final. Pour cette raison, vous voyez souvent la classe d'activateur apparaître en couche C tranchante dans une couche non critique. y a absolument rien de mal à utiliser un peu de réflexion pour répondre à vos besoins. Mais qu'en est-il du froid critique, où les performances optimales sont cruciales ? Heureusement, il existe une autre solution. Je vais vous montrer un tour de souffle pour construire n'importe quel type de classe au moment de l'exécution avec une performance qui est comparable aux instructions switch. Vérifie ça. abord, revenons au problème de base dont nous avons besoin pour compléter cette méthode. C' est une méthode génétique qui peut construire à tout moment, mais une collection de méthodes spécifiques serait également bien. Disons donc que j'ai besoin de construire une classe appelée ma classe de type 1. Ensuite, j'aurais besoin du message suivant dans un langage intermédiaire commun. Le corps du message va ressembler à ça. Donc voici l'invité hors du tour. Je vais créer dynamiquement un délégué à l'exécution à droite ces deux instructions de
langue intermédiaire dedans, puis appeler les délégués pour créer l'instance de classe. Voici comment ça marche. Ma fabrique de classes reçoit d'abord la chaîne de nom de type, qui décrit le type de l'instance de classe à créer. La première chose que l'usine de classe fait est de vérifier dans un dictionnaire si les
délégués correspondants ont déjà été créés. Si c'est
le cas, l'usine récupère les délégués du dictionnaire et l'appelle directement pour créer la classe. Instance sait même que l'usine crée une méthode dynamique sur écrit les nouveaux objets et y
retourne des instructions. Il enveloppe ensuite les méthodes dans un délégué et stocke les délégués dans le dictionnaire au cas où
nous en aurions besoin à nouveau plus tard. Enfin, il appelle les délégués pour créer l'instance de classe. La création d'une méthode dynamique délicate est vraiment lente, encore plus lente que l'appel de la classe activator. Mais voici le truc. Une fois que nous avons un délégué, nous pouvons continuer à l'appeler encore et encore pour créer plus d'instances de classe. Andi appeler un délégué est beaucoup, beaucoup plus rapide que d'utiliser la classe activator, donc il n'y aura qu'un retard lors de la création des premiers objets à partir d'un type donné. Tout charbon ultérieur va être super rapide. Jetons un coup d'oeil à mon programme de benchmarking pour cette conférence. Vous pouvez voir dans cette déclaration constante ici que je vais m'intéresser, et elle a mangé un million d'objets. Le premier message mesure une boucle un million de fois sur utilise les instructions switch statiques pour construire les objets que vous pouvez voir ici. Que mon programme crée des instances de constructeur de chaînes sur ne supporte vraiment rien d'autre. Donc, les instructions switch sont un peu stupides car il ne prend en charge qu'une seule classe sur ne peut pas être modifiée de quelque manière que ce soit après la copulation. Mais nous garderons son ici pour servir de valeur de référence. Cela signifie la meilleure performance possible lors de la création d'un million d'objets. Ensuite, Measure B, qui utilise la classe activator pour créer les constructeurs de chaînes vraiment simples et propres codes sur. C' est vraiment dommage que ça ne se produise pas très bien. Vous allez voir dans un instant à quel point la réflexion est lente. Enfin, voici la mesure C. Elle utilise une usine de classe appelée obtient le créateur de classe. Cette usine de classe retourne un créateur de classe, que vous pouvez voir est en fait un délégué pour une méthode sans aucun paramètre ne retourne pas un objet. La ligne suivante crée ces générateurs de chaînes en appelant simplement les délégués. Donc toute la magie se passe dans l'usine de classe. La première chose que fait l'usine de classe est d'essayer de récupérer les délégués du dictionnaire . Si le dictionnaire ne contient pas encore les délégués, l'usine se déplace sur obtient les informations du constructeur pour le constructeur de chaînes. Il crée ensuite une méthode dynamique. Dan écrit le nouvel objet sur les instructions de retour dans la méthode. Enfin, il enveloppe les méthodes dans un délégué sur les magasins. Les délégués dans le dictionnaire finalement ici les principales méthodes de programme appelle les trois méthodes de
mesure et affiche votre propre temps en millisecondes. Ok, voyons comment ces trois techniques se mesurent. Je vais diriger le programme. Vérifiez ceci et voici les résultats. L' instruction switch s'est exécutée en 131 millisecondes. La classe activateur parle 11,377 millisecondes sur la méthode dynamique parlent 673 millisecondes. Voici les résultats dans un graphique. Donc, l'instruction switch a construit un million de constructeurs de chaînes en 131 millisecondes. Cela correspond à 7633 objets créés paire millisecondes, mais la classe activateur a fait
beaucoup, bien pire. Il a fallu 11 377 millisecondes pour construire les constructeurs de chaînes, ce qui correspond à seulement 87 objets pendant des millisecondes. La classe activator est 86 fois plus lente que les instructions switch. Et enfin, voici les résultats du message dynamique. Le délégué des méthodes dynamiques a construit un million de constructeurs de chaînes en 673 millisecondes. Cela correspond à 1485 objets pendant des millisecondes, sorte que les méthodes dynamiques sont cinq fois plus lentes que les instructions switch. Mais c'est presque 17 fois plus rapide que la classe activateur. Ok, alors qu'avons-nous appris ? Une usine de classe est une classe qui construit d'autres instances de classe à la demande à l'aide d'informations
de configuration externes . Une façon courante d'implémenter des usines de classe est avec la classe activator, mais la classe activator est 86 fois plus lente que statique compilée. Manteau. Une bien meilleure solution est d'utiliser des méthodes dynamiques, délègue les méthodes dynamiques. Les délégués sont seulement cinq fois plus lents que les codes statiques compilent 17 fois plus vite que la classe
activateur. En d'autres termes, si vous remplacez la classe activator par des délégués de message dynamique dans votre usine de classe, vous n'allez pas accélérer votre couche d'un facteur de réduction de 17.
20. Les tableaux de arrays sont-ils dignes de l'ennui ?: Dans les deux dernières conférences,
nous avons examiné les classes de collection, y compris la liste des tableaux, la liste générique sur plusieurs types d'élévation native, une dimension, deux dimensions et des tableaux dentelés. Il y a une chose que toutes ces classes de collection ont en commun. Ils vivent tous sur le tas. Mais si je vous disais que c'est possible en C ? L' orteil pointu alloue sur un tableau d'interviewers directement sur la pile, ayant une collection d'entiers hors de la pile comme avantages. Si vous regardez le tas,
le tas est géré de la mémoire. Cela signifie que le garbage collector déplace occasionnellement des blocs de mémoire autour de l'orteil optimisent l'espace libre. Mais cette pile fonctionne différemment. mémoire sur la pile est allouée lorsque votre code entre dans une méthode en tant que D alloué. Lorsque vous revenez d'une méthode allouée, la mémoire n'est jamais déplacée. Cela a un avantage important. Si nous pouvions obtenir un pointeur pour empiler la mémoire, il restera valide jusqu'à ce que la mémoire de pile soit la mémoire allouée. Mais obtenir un pointeur vers la mémoire de chaleur est beaucoup plus difficile parce que le garbage collector peut déplacer la mémoire ailleurs à tout moment pour obtenir un point ou deux mémoire de chaleur avec avoir à fixer un bloc de mémoire en place sur le tas, dire au garbage collector Vous ne pouvez pas toucher ce bloc de mémoire jusqu'à ce que j'en ai terminé toute cette gestion de la mémoire crée une surcharge de performances, il n'est
donc pas déraisonnable de s'attendre à ce que le point d'opérations sur la pile soit
plus rapide que le même point d'opérations sur le Donc, regardons un peu de code à nouveau. Voici le programme que j'ai utilisé précédemment pour comparer les performances hors de la liste de tableau la liste
générique sur le tableau natif. Mais j'ai ajouté une autre méthode de test à la mesure inférieure D.
Jetons un coup d'oeil. Je commence par déclarer sur des codes dangereux. Bloc en C. Point d'opérations
pointu ne sont possibles qu'à l'intérieur de blocs dangereux, il s'agit donc d'une étape obligatoire. Ensuite, j'utilise ces mots clés coincés Un regard pour allouer sur le tableau de 10.000 entiers directement sur la pile. La valeur de retour de cette opération est sur Interviewer Pointer qui pointe et directement dans mémoire de
pile. Puis deux imbriqués lâche. La boucle externe répète les expériences 10 000 fois. Le regard intérieur traverse tous les 10 000 éléments du tableau et de la science
chacun d'entre eux. Vous voyez que même si list est un interviewer, Pointer, la syntaxe, accès aux
orteils et les éléments est exactement la même que si la liste serait un tableau entier. Les crochets s'effacent. Intacs travaille toujours ici. Voici les méthodes de mesure C. Cette méthode fait exactement la même opération, mais elle utilise un tableau entier régulier sur le tas afin que nous puissions comparer les performances. Laissons les codes. On y va. L' entier régulier de Ray prend 808 millisecondes pour s'exécuter, mais le tableau Stack n'a besoin que de 764 millisecondes. Le tableau Stack est cinq points, 4% plus rapide. Jetons un coup d'oeil dans les coulisses et voyons ce qui se passe dans le langage intermédiaire. Je vais basculer les environnements vers les points de rupture du centre de mode de débogage, exécuter à nouveau le code, puis passer à la vue de démontage afin que nous puissions voir les instructions de
langue intermédiaire sur. Et voici les codes pour les affectations de tableau d'entiers réguliers. Vous voyez qu'il s'agit de l'arrangement familier de trois instructions d'emplacement de chargement et d'un élément
de jeu. La variable de liste est stockée à l'emplacement de la variable. Un sur est chargé sur la pile d'évaluation, puis le I Valuable est chargé deux
fois, une fois comme index du tableau et une fois comme valeur à définir. Enfin, la construction des éléments de jeu stocke la valeur à l'index donné. Regardons maintenant l'opération Poynter dans les codes gérés. Si je fais défiler, voici les mêmes affectations dans les méthodes de mesure D. abord, le pointeur de liste est chargé sur la pile d'évaluation,
Ensuite, la variable I sur le numéro quatre est chargée, multipliée ensemble et ajoutée à la valeur de liste. Cela a du sens si vous considérez qu'un entier est quatre morsures en mémoire, le manteau calcule simplement l'adresse mémoire à écrire, qui est la liste plus pourquoi multiplié par quatre. L' instruction suivante est load Location three, qui charge la valeur à écrire sur la pile d'évaluation. Et enfin, nous avons un magasin en instruction directe à ce sujet. Instruction écrit la valeur directement dans la mémoire. Donc, peut-être étonnamment, ces sept points ou instructions fonctionnent en fait plus vite que les quatre instructions de tableau dans la mesure. C Méthode sur la différence est parce que les éléments ensemble droits d'instruction dans la
mémoire gérée , tandis que le magasin dans l'instruction directe écrit directement dans une adresse de mémoire fixe sans avoir à prendre aucune sorte de gestion de la mémoire en compte. Enfin, permettez-moi de relancer le programme. Mais cette fois, avec toutes les mesures, méthodes permettent, je vais comparer la liste de tableaux, la liste génétique, le tableau d'interview natif, les mains de l'entier ou un sur la pile. On y va. Et voici les résultats. Laissez-moi vous montrer un graphique des résultats, qu'ils soient faciles à comparer. L' exécution de la liste des tableaux prend 9314 millisecondes. La liste générique est beaucoup plus rapide. C' est 2704 millisecondes. Le tableau entier est encore plus rapide. C' est seulement 745 millisecondes. Mais ce tableau de pile bat tout le monde avec un temps d'exécution hors seulement sept centaines et 13 millisecondes. La différence de performance entre le tableau entier natif sur le tableau sur la pile est 4 %. C' est beaucoup moins que la différence entre, disons, le tableau entier sur la liste générique, qui est de 72%. Donc, si vous optimisez certains codes en remplaçant une liste génétique par le tableau natif, ce qui vous donne déjà un gain de performance de 70%, vaut-il la peine d'aller encore plus loin et de transformer le tableau en un tableau basé sur la pile juste pour le gain supplémentaire de seulement 4 à 5%. La réponse est probablement pas. La différence de performance est si faible qu'il pourrait facilement être un coup de mesure. Pour tester ce scénario, j'ai apporté quelques modifications à mon programme. Jetons un coup d'oeil. Je trouve simplement le programme pour ne regarder que le tableau régulier sur la pile nécessaire chaque méthode de
mesure attend paramètre qui indique le nombre d'éléments de tableau à
allouer . La mesure est répétée 5000 fois sur chaque méthode retourne au temps d'exécution total en millisecondes. Voici les principales méthodes du programme, avec la boucle qui répète l'expérience pour les nombres de différence hors éléments du tableau. Alors, quels sont les résultats ? J' ai effectivement préparé les résultats à l'avance sur les mettre dans un graphique. Voici donc la performance pour les deux types de course pour les tailles de course de 0 à 49 éléments. Assez peu concluant, diriez-vous ? Mais peut-être que la différence de performance devient significative comme des tailles de tableau plus grandes. Alors testons ça aussi. Voici les résultats à nouveau, mais maintenant, pour une course tailles entre un milliers sur 100 000 éléments encore peu concluants. n'y a pas de gagnant clair. Alors, quand devriez-vous utiliser Stack basé sur la race ? La réponse est presque jamais, certainement pas pour l'optimisation des performances, les améliorations sont minuscules, seulement quelques pour cent, et très souvent le tableau basé sur la pile est en fait plus lent qu'un tableau basé sur la chaleur ordinaire. Eso. La taille de la pile est limitée. Les tableaux basés sur la pile ne peuvent stocker jusqu'à 10 mégaoctets de données avant de manquer de Stacks Base. Pour vous donner une idée, j'ai essayé d'allouer 10 millions d'entiers sur la pile. J' ai une exception de débordement de pile. Donc vous vous demandez peut-être, Pourquoi cette pile est-elle une serrure ? Mots clés même disponibles ? Eh bien, la réponse est de fournir sur l'interface à d'autres langages de programmation qui alloue des tableaux sur la pile. Lorsque vous écrivez manteau en bord de mer qui doit interfacer avec le Windows un P I ou bibliothèques écrites en C ou C plus, plus le stock. Alex Keywords vous permet de configurer un tampon de mémoire comme une sorte de passerelle entre ces langues sur le dot net gérer la mémoire. Donc, en conclusion, utilisez
seulement empiler un verrou pour l'interfaçage avec couche non gérée sur. Ne l'utilisez pas pour optimiser un tableau car cela ne vaut probablement pas la peine.
21. Comment utiliser les pointeurs dans C# ?: dans cette conférence, je vais parler de pointeurs maintenant. Les pointeurs ne sont pas très utilisés, mais le langage C Sharp les prend en charge, mais par défaut, les points d'opérations sont interdits car ils sont potentiellement dangereux. Si vous utilisez des pointeurs dans votre manteau, le temps d'exécution du beignet ne peut plus garantir que la mémoire ne sera jamais corrompue. Et c'est pourquoi ils sont interdits par défaut. Cependant, les opérations
ponctuelles deviennent disponibles. Si vous allez dans les propriétés de votre projet sur activer manteau dangereux, alors vous pouvez utiliser les déclarations et les déclarations suivantes dans votre manteau. Les mots-clés non sécurisés activent le point d'opérations. Vous pouvez utiliser les mots-clés pour marquer les blocs hors couche qui utilisent des pointeurs. Ou vous pouvez placer les mots-clés dans une déclaration de méthode pour rendre ces méthodes entières dangereuses. Lorsque vous déclarez une variable en ajoutant et Astérix à la fin du type, vous déclarez un pointeur hors de cette heure donnée. Donc, dans ce cas, je déclare un point de morsure ou les mots-clés fixes, broches et des objets en mémoire, disant au garbage collector de ne pas déplacer les objets autour de la chaleur jusqu'à ce que nous ayons fini avec. Étant donné que l'objet est corrigé, vous pouvez ensuite utiliser l'opérateur d'un pourcentage pour obtenir l'adresse mémoire des objets. Donc, dans cet exemple, je fixe sur des objets dans la variable Q sur la chaleur, puis j'obtiens son adresse avec l'opérateur et pour cent, puis un signe que l'adresse de mémoire au pointeur bys dans la variable p, la variable Astérix opérateur de références nommé, ce qui signifie accéder aux données que cet emplacement de mémoire. Donc, dans cet exemple, je lis une seule morsure à l'adresse actuelle du pointeur. Vous pouvez également traiter les pointeurs comme une course. L' opération d'indexation lit les données à on décalages à l'adresse Pointeur Courants. Dans cet exemple, je lis une seule morsure à l'emplacement de mémoire. Deux piqûres devant l'adresse actuelle hors du point. Ou vous pouvez modifier l'adresse mémoire du pointeur en ajoutant simplement ou en soustrayant de son. Dans cet exemple, je suis en train de mettre à jour l'adresse mémoire actuelle du pointeur en l'avançant trois bouchées devant lui. Le point de soutien a été ajouté au temps d'exécution dominant pour trois raisons. Un deux aide l'interopérabilité avec du code non managé pour prendre en charge un C ou C plus. Plus ne pas que c'est compiler et trois pour le rendre facile point de port orteil des algorithmes basés pour voir forte. Donc devrait être utilisé pointeurs en C Sharp ou tout simplement coller à une course. Eh bien, découvrons. L' exemple classique pour utiliser des pointeurs dans C. Sharp est lors de la manipulation d'image. images Lesimageschargées en mémoire sont gérées par le système d'exploitation. Les données de l'image sont dans la mémoire non gérée comme pas sur le tas d'obscurité. Il n'y a donc que deux façons d'interagir avec les données de l'image. Ils utilisent des villes de pixels de haut niveau, définissent des méthodes de pixel ou en obtenant un pointeur vers la mémoire non gérée. J' ai écrit un petit programme qui charge l'image en mémoire, puis effectue une
transformation en niveaux de gris sur son Jetons un coup d'oeil. Je vais commencer par les principales méthodes ici. Mon image de test est une photo sur 19 années 70 mannequin, affirme Burke. Cette image est la norme d'or pour tester les algorithmes de traitement d'image. J' aime l'image en mémoire, puis appelé la mesure un désordre est d'effectuer la conversion en niveaux de gris en utilisant les méthodes get pixel et définir excell, puis écrire les résultats sur le disque. Ensuite, je fais tout à nouveau, mais cette fois j'appelle la mesure Be Message, qui utilise le point d'opérations pour effectuer la conversion en niveaux de gris vers le haut. Voici les méthodes de conversion. mesure A reçoit l'image sous forme de boucles d'un paramètre à travers chaque pixel. En utilisant ces 24 perdre contre la couleur du pixel avec obtenir pixel médical. La formule ici effectue la conversion en échelle de gris. Ensuite, je crée une nouvelle valeur de couleur, en utilisant la variable de niveaux de gris et en modifiant le pixel de courants en appelant Seth Pixel. Maintenant, comparez cela avec la mesure B. Je commence par un appel à verrouiller les bits qui demande l'accès au dater d'image brute. Puis, dans ce bloc de voitures dangereuses, j'ai obtenu un pointeur de morsure vers les données de l'image en appelant les deux méthodes de pointeur. La formule de conversion en niveaux de gris est ici note que j'utilise une salle d'index pour obtenir les valeurs rouges vert et bleu à l'adresse de point actuelle sur et un et deux morsures devant l' adresse
actuelle. Ce prochain verrou de codes écrit la valeur de niveaux de gris dans les valeurs de pixel rouge, vert et bleu en écrivant à plusieurs reprises au point d'adresse actuel avançant le pointeur d'une mordure. Et enfin, je déverrouille les données de l'image au retour de l'exécution en millisecondes. Alors quelle méthode va être plus rapide, vous pensez ? Laisse-moi diriger le programme et le savoir maintenant. Comme je l'ai déjà dit, je cours un programme de capture d'écran en ce moment sur ce programme met la pression sur le CPU sur mémoire libre
disponible. Sur cela déforme les valeurs de performance. J' ai donc préparé cette section à l'avance et mis les valeurs de performance non déformées dans un graphique. Voici les résultats que j'exécute le test trois fois comme moteur d'exécution pour effectuer une
conversion en niveaux de gris . En utilisant get pixel et set pixel est entre 146 ons 184 millisecondes. Mais lors de l'utilisation de pointeurs, l'exécution tombe à seulement sept millisecondes. Pour les trois tests, le point de fonctionnement est un écrasant 96% plus rapide que la
méthode de pixel du combiné get pixel . Enfin, permettez-moi de vous montrer les résultats. Je vais ouvrir un navigateur de fichiers sur sélectionner l'image originale et l'image ressemble à ceci. Les sorties de la mesure A et de la mesure B sont ici. Comme vous pouvez le voir, il s'agit d'une conversion parfaite en niveaux de gris sur les sorties hors tension. Les deux méthodes sont identiques. Nous avons donc examiné la manipulation d'image en C net et découvert que les pointeurs donnent un énorme pouce de performance de
96 %. Est-il acceptable d'utiliser des pointeurs ? Dans ce scénario, la réponse est oui. La principale à emporter pour cette conférence est toujours utilisée. Pointeurs pour la manipulation d'image dans dot net. Vous n'avez pas vraiment le choix car les données de l'image sont dans la mémoire non gérée. Sur l'alternative, obtenir le pixel et définir les méthodes de pixel sont beaucoup trop lentes. Je peux généraliser ce conseil dans les lignes directrices suivantes. abord, si vous interagissez avec des données dans la mémoire non gérée, essayez d'utiliser des méthodes get et set de haut niveau pour lire et écrire les données. Cela évitera d'avoir à utiliser les mots-clés non sécurisés sur Marquer votre assemblage comme dangereux. Si cette route n'est pas acceptable parce que vous devez lire et écrire une grande quantité de données sur les têtes au-dessus des méthodes get and set individuelles devient trop grande. Zen. Obtenez un pointeur vers les données à la place en modifiant la mémoire directement pour la
manipulation d'image . Utilisez toujours des pointeurs parce que les jours d'image, je serai toujours trop grand pour tout autre type
d' accès de haut niveau. C' est une pratique acceptée parfaite pour la manipulation d'images. Bibliothèques écrites en C sharp pour être dangereuses
22. Astuce #10 : Tip le code avec les pode: Dans la conférence précédente, nous avons examiné l'utilisation de pointeurs pour la manipulation d'image en C. Maintenant, la manipulation
d'image se qualifie comme un scénario où nous interagissons avec du code non géré. Le système d'exploitation charge la mémoire des images sur son propre tas non géré. Et la seule façon d'obtenir les données de l'image est de demander un point de morsure ou plus pour ce scénario
spécifique. Bien sûr, nous pouvons utiliser des pointeurs. Mais qu'en est-il des autres cas d'utilisation ? Est-il utile d'implémenter des structures de données comme des listes liées et des arbres binaires avec des pointeurs ? Ou devrait-on s'en tenir à une course pour le savoir ? Nous devons comparer avec précision les performances hors baie et les opérations de pointeur pour voir s'il y a une différence entre elles. Imaginons donc un instant que tout mon système d'exploitation est réellement écrit en C. Sharp. Je cours ces codes sur une macro pro, et je vais faire semblant que le système d'exploitation toujours X est réellement écrit en C sharp . Donc, quand je charge sur l'image dans la mémoire, les données de l'image sont immédiatement disponibles en tant que géré par tary. J' ai donc écrit un deuxième programme basé sur cette hypothèse. Jetez un oeil à ce Commençons par les premières méthodes de mesure A. Mon tableau de morsures est ici, Andi. Je prétendrai que c'est les données d'image chargées dans ce tableau. Je boucle à travers le tableau sur les converts, chaque pixel en échelle de gris, utilisant la formule familière. Donc, ce culte utilise l'indexation de tableau traditionnelle pour convertir chaque pixel. Maintenant, voici le même code est réécrit en utilisant des pointeurs. J' ai utilisé les mots-clés fixes pour pincer le tableau en mémoire, puis j'obtiens l'adresse des premiers éléments du tableau avec l'opérateur 1%, et j'ai affecté cette adresse au pointeur de morsure dans la variable P. Le reste de la couche est exactement la même que les codes basés sur le tableau. Je regarde chaque pixel avec cette variable par et utilisé l'indexation du pointeur pour lire et écrire chacun par sa valeur. Et enfin, voici un autre point de méthodes basées. Mais au lieu de désactiver, en utilisant l'accès aux orteils d'indexation, chaque pixel, je fais simplement avancer le pointeur lui-même. Chaque boucle interationnelle lit uniquement les rouges verts sur les valeurs bleues, les
modifie en échelle de gris. Ensuite, j'ai avancé le pointeur de trois piqûres pour accéder au pixel suivant, donc il y a encore une indexation dans cette méthode, mais seulement par 01 ou deux piqûres. Les principales méthodes ici appelle les trois méthodes pour différentes tailles d'image. Je mesure la performance pour les images simulées de 512 jusqu'à 4096 pixels de large par pas 100 28. Maintenant, comme je l'ai mentionné précédemment, le logiciel de capture d'écran déforme mes mesures, donc j'ai préparé les résultats à l'avance. Voici les résultats. Comme vous pouvez le voir, il n'y a pas beaucoup de différence entre la mesure A, la mesure B, en utilisant un tableau de brûlure ou achète pointeur avec indexation, a plus ou moins des performances égales. Mais regardez la mesure C constamment plus rapidement que les deux autres, de 25 % en moyenne. Alors pourquoi la mesure est voir beaucoup plus vite ? Pour le savoir, nous devons jeter un oeil au code intermédiaire. Laissez-moi donc m'assurer que le projet est en modes de débogage. Je vais mettre des points de rupture dans les méthodes principales, puis autour du programme sur le passage à la vue de démontage. Alors on y va. Je vais commencer par la mesure, un message qui utilise un régulier géré par tary. Le code qui modifie les pixels est ici. Vous pouvez voir. Ce sont les trois instructions de chargement familières, puis une instruction d'élément de magasin pour modifier les éléments du tableau. Le tableau d'images est à l'emplacement zéro. La variable d'index I est à l'emplacement de, car la valeur à écrire est à l'emplacement trois. Nous l'avons vu de nombreuses fois. En ce moment. Cette ligne de codes modifie la valeur de couleur suivante. C' est presque la même couche, mais la variable I est incriminée par un avec ces trois emplacements de charge pour charger Constant un sur ajouter des instructions sur la troisième ligne de codes est presque la même. Mais maintenant, la variable je l'incrémente de deux. Et enfin, ici, la variable I est incriminée par trois avec ces quatre emplacements de charge réduit les ums constants sur les instructions d'emplacement du
magasin. Alors, comment ce manteau ressemble-t-il quand on utilise des pointeurs ? Laissez-moi faire défiler jusqu'à la mesure, être des méthodes et trouver les lignes correspondantes de couche Tout d'abord, le pointeur P est chargé avec cet emplacement de charge à l'instruction, puis la variable. Je suis chargé avec cette note Emplacement trois instructions sur les factures sont ajoutées ensemble pour obtenir l'adresse mémoire à écrire. Enfin, la valeur à écrire est chargée avec cet emplacement de chargement pour instruction. Le magasin final dans l'instruction directe effectue le droit réel à la mémoire. Vous voyez que cette séquence hors instructions est presque la même, sauf qu'il y a un supplément sur l'instruction pour calculer l'adresse mémoire et que la valeur est écrite avec un magasin dans l'instruction directe au lieu d'une instruction d'élément de magasin . Les deux lignes suivantes sont presque les mêmes, sauf pour l'instruction supplémentaire ajoute soit un ou deux à la variable I. Maintenant, regardons. C' est une mesure C. L' écriture à la mémoire est juste ces trois instructions aime l'emplacement. Trois. Pour charger les points sont l'emplacement de charge cinq à bas. La valeur en niveaux de gris Andi stocke indirecte pour écrire dans la mémoire. Les lignes suivantes ont ces deux instructions supplémentaires charge constante une et ajoute à l'incrément nommé par un et la constance de charge aussi, et ajouter à incrémenter le pointeur de deux. Enfin, le pointeur l'incrémente de trois avec ce froid ici, qui est identique au culte pour incriminer la variable I dans les deux autres méthodes. Alors pourquoi ce rhume est-il plus rapide ? Eh bien, pour commencer, cette couche a moins d'instructions de langue intermédiaire que les deux autres méthodes de mesure être nécessaires. Cinq à des opérations pour modifier le tableau où ce culte n'a besoin que d'ajouter des opérations et mesurer un besoin. Trois instructions d'emplacement de chargement avant d'appeler les éléments de magasin où cette couche n'a besoin que des instructions. En outre, le point Traditions sont beaucoup plus petites dans la mesure être. Nous devons ajouter la variable I au point de P pour obtenir l'adresse de mémoire comme la fin de
la boucle, je passe à de très grandes valeurs jusqu'à 48 millions. Comparez ceci aux méthodes. Voyez où se trouve le pointeur. Seul l'incrément est de 12 ou trois piqûres. Les ajouts de petits nombres sont beaucoup plus efficaces que les ajouts de grand nombre parce
que l'utilisation de CP a des instructions spécialisées pour augmenter la valeur d'un. Qu' est-ce qu'on peut dire ? En conclusion ? Quand utilise des pointeurs valets Choice. Nous avons vu que géré une augmentation sur le point d'index des opérations effectuées également dans dot net. Mais pour les valeurs d'index de très grande taille, il est beaucoup plus efficace d'incrémenter et de lire et d'écrire des données à proximité de l'adresse mémoire du point ou des courants. Et c'est donc la principale leçon à emporter pour cette conférence. Normalement, vous ne devriez pas utiliser de pointeurs en C sharp parce que les gains de performance ne valent tout simplement pas la
peine . Les pointeurs d'index et les tableaux unidimensionnels réguliers ont des performances égales. Pour éviter d'avoir à utiliser un manteau dangereux, vous devriez choisir une course. Comment se fait-il dans des scénarios spécifiques ? Le point de l'algorithme basé peut surpasser un algorithme basé sur un tableau jusqu'à 25% pour ces scénarios, Les conditions suivantes doivent être remplies. abord, vous lisez et écrivez un gros bloc de données d'au moins 10 mégaoctets de taille ou plus. Deuxièmement, l'algorithme scanne chaque morsure séquentiellement ou saute à travers les données d'une
manière prévisible et les épées au cours de chaque boucle. Seules les données d'itération très proches de l'adresse actuelle du pointeur doivent être lues ou écrites. Si votre algorithme adhère à ces conditions et que vous ne vous dérangez pas de produire un
assemblage dangereux , hésitez pas à utiliser des pointeurs et à récupérer les 25% supplémentaires de performance
23. recap de cours: félicitations. Vous avez terminé tout le cours. Vous êtes maintenant un optimiseur de code C pointu certifié. Je vous ai montré environ 25 types différents d'optimisation des appels. Comment les utiliser dans votre propre usine de manteau, quelles améliorations de performance spécifiques ils vous apporteront. L' optimisation est étalée sur trois sections. Nous avons eu l'optimisation de base, le fruit à faible pendaison où une simple modification de votre manteau pourrait entraîner un énorme plis de 100 Amélioration des performances. . Nous avons les organisations intermédiaires qui étaient plus complexes, appelées les droits qui offrent seulement une petite à moyenne des améliorations sur. Nous avons eu à l'optimisation avancée du noyau dur où nous avons utilisé plusieurs fonctionnalités exotiques hors du cadre
Net pour augmenter les performances. Ces optimisations vous ont donné une riche boîte à outils de connaissances et d'idées que vous pouvez utiliser lors de l'écriture de votre propre manteau ou lorsque vous collaborez au sein d'une équipe de développement, surtout si vous travaillez sur des codes critiques où les performances les plus élevées sont cruciale. Et si vous avez découvert une nouvelle technique de performance intéressante de votre propre police, partagez-les dans la discussion de cours pour lui, pour que nous puissions tous profiter. J' espère que vous apprécierez le cours sur appris quelques nouvelles techniques utiles que vous pouvez appliquer dans votre carrière de développement logiciel Maintenant allez et construire de grandes choses