So schreibt man sehr schnell C# Code | Mark Farragher | Skillshare

Playback-Geschwindigkeit


  • 0.5x
  • 1x (normal)
  • 1.25x
  • 1.5x
  • 2x

So schreibt man sehr schnell C# Code

teacher avatar Mark Farragher, Microsoft Certified Trainer

Schau dir diesen Kurs und Tausende anderer Kurse an

Erhalte unbegrenzten Zugang zu allen Kursen
Lerne von Branchenführern, Ikonen und erfahrenen Experten
Eine Vielzahl von Themen, wie Illustration, Design, Fotografie, Animation und mehr

Schau dir diesen Kurs und Tausende anderer Kurse an

Erhalte unbegrenzten Zugang zu allen Kursen
Lerne von Branchenführern, Ikonen und erfahrenen Experten
Eine Vielzahl von Themen, wie Illustration, Design, Fotografie, Animation und mehr

Einheiten dieses Kurses

23 Einheiten (3 Std. 43 Min.)
    • 1. C# Performance Tipps

      2:59
    • 2. Einführung in die Codeoptimierung

      6:12
    • 3. Was ist Stapelspeicher?

      5:23
    • 4. Was ist Haufen Speicher?

      6:18
    • 5. Was sind value

      5:50
    • 6. Was sind Referenztypen?

      5:29
    • 7. Was ist das Boxen und Auspacken?

      6:28
    • 8. Was sind unveränderliche Zeichenfolgen?

      6:43
    • 9. Ein Crashkurs in der Zwischensprache

      14:45
    • 10. Tipp #1: Verhindern Sie das Auspacken und Auspacken

      9:39
    • 11. Tipp #2: Zeichenfolgen effizient zusammenfügen

      8:20
    • 12. Tipp #3: Verwenden der richtigen list

      8:29
    • 13. Tipp #4: Verwenden des richtigen #4:

      9:09
    • 14. Tipp #5: Vermeiden von Ausnahmen

      14:55
    • 15. Tipp #6: Verwenden Sie für anstatt für jedes Mal

      16:40
    • 16. Wie funktioniert der Müllsammler?

      16:07
    • 17. Tipp #7: Optimierung für die Müllsammlung

      18:26
    • 18. Tipp #8: Schnellste Delegierte verwenden

      9:13
    • 19. Tipp #9: Eine schnelle class bauen

      17:06
    • 20. Sind die Arrays auf dem Stapel die Schwierigkeiten wert?

      11:28
    • 21. Wie verwende ich Zeiger in C#?

      10:05
    • 22. Tipp #10: Code mit Zeigern beschleunigen

      11:43
    • 23. Kursinformationen

      1:44
  • --
  • Anfänger-Niveau
  • Fortgeschrittenes Niveau
  • Fortgeschrittenes Niveau
  • Alle Niveaus

Von der Community generiert

Das Niveau wird anhand der mehrheitlichen Meinung der Teilnehmer:innen bestimmt, die diesen Kurs bewertet haben. Bis das Feedback von mindestens 5 Teilnehmer:innen eingegangen ist, wird die Empfehlung der Kursleiter:innen angezeigt.

193

Teilnehmer:innen

--

Projekte

Über diesen Kurs

78e5abec

Wissen Sie, wie man einen schnellen C#-Code schreiben?

Vielleicht hast du dich in einem C# unterrichtet oder die Sprache in der Schule oder der Universität gelernt. Aber hier ist eine sobering Tatsache: Die meisten Kurse lehren dir, wie du Code schreiben kannst, nicht wie du schneller Code schreiben kannst.

Die . NETZ Framework ist riesig. Für jedes Problem gibt es viele Lösungen, und es ist nicht immer klar, welche Lösung die beste Wahl ist.

Wusstest du, dass die falsche Zeichenfolge den Code um einen Faktor von mehr als zweihundert verlangsamt werden? Und wenn du Ausnahmen nicht so richtig handling dann läuft dein way, tausendmal langsamer als normal.

C# Code ist ein großes Problem. Langsamer Code im Internet wird nicht auf Tausende von Benutzern skalieren. Langsamer Code macht deine Benutzeroberfläche unbrauchbar. Langsamer Code lässt deine mobilen Apps im App-Store schmachten.

Langsamer Code hält dich zurück!

Ich kann dir helfen.

In einer Reihe von kurzen Vorträgen decke ich viele gemeinsame Performance-Engpässe ab. Ich stelle jedes Problem vor und schreibe dann ein kleines Testprogramm, um die Ausgangsleistung zu messen. Dann zeige ich jede mögliche Lösung und beurteile die Messung jeder Lösung an.

Aber es gibt noch mehr! Ich tauche auch in den Common Intermediate Code (CIL) ein, der Sprache, in der der C# kompiliert. Wenn das beängstigend klingt, mach dir keine Sorgen! Die CIL ist eigentlich sehr einfach zu lesen und zu verstehen. Ich bringe dich in einem kurzen 15-minütigen Vortrag durch die Grundlagen

CIL lesen ist eine sehr nützliche Fertigkeit, die dir dabei hilft, viele performance zu vermeiden und dir ein tieferes Verständnis der Dinge zu geben. NET

Warum solltest du diesen Kurs machen?

Du solltest diesen Kurs machen, wenn du ein Anfänger oder ein intermediärer C#-Entwickler bist und deine Fähigkeiten auf die nächste Stufe bringen möchtest. Alle meine Vorträge sind sehr einfach zu befolgen und ich erkläre alle Themen mit klarem Code und vielen lehrreichen Diagrammen.

Oder du wirst vielleicht an einem kritischen Codeabschnitt in einem C# arbeiten und musst deinen Code so schnell wie möglich ausführen lassen. Die Tipps und Tricks in diesem Kurs helfen dir immens.

Oder vielleicht bereiten Sie sich auf ein C# angebrachtes Vorstellungsgespräch vor? Dieser Kurs gibt dir eine hervorragende Grundlage für alle aufschlussreichen Fragen zu beantworten, die du vielleicht ansprechen kannst.

Triff deine:n Kursleiter:in

Teacher Profile Image

Mark Farragher

Microsoft Certified Trainer

Kursleiter:in

Mark Farragher is a blogger, investor, serial entrepreneur, and the author of 11 successful Udemy courses. He has been a Founder and CTO, and has launched two startups in the Netherlands. Mark became a Microsoft Certified Trainer in 2005. Today he uses his extensive knowledge to help tech professionals with their leadership, communication, and technical skills.

Vollständiges Profil ansehen

Kursbewertungen

Erwartungen erfüllt?
    Voll und ganz!
  • 0%
  • Ja
  • 0%
  • Teils teils
  • 0%
  • Eher nicht
  • 0%
Bewertungsarchiv

Im Oktober 2018 haben wir unser Bewertungssystem aktualisiert, um das Verfahren unserer Feedback-Erhebung zu verbessern. Nachfolgend die Bewertungen, die vor diesem Update verfasst wurden.

Warum lohnt sich eine Mitgliedschaft bei Skillshare?

Nimm an prämierten Skillshare Original-Kursen teil

Jeder Kurs setzt sich aus kurzen Einheiten und praktischen Übungsprojekten zusammen

Mit deiner Mitgliedschaft unterstützt du die Kursleiter:innen bei Skillshare

Lerne von überall aus

Ob auf dem Weg zur Arbeit, zur Uni oder im Flieger - streame oder lade Kurse herunter mit der Skillshare-App und lerne, wo auch immer du möchtest.

Transkripte

1. C# Performance-Tipps: Lassen Sie mich Ihnen eine Frage stellen. Möchten Sie ein C scharfer Performance-Architekt werden? Okay, ich muss zugeben, dass ich das Wort erfunden habe. Aber in meinem Buch ist ein C scharfer Performance-Architekt ein Senior Entwickler, der Hochleistungs-C scharfen Mantel schreibt . Also ein Senior Entwickler, der akut bewusst Off-Coat-Optimierungstechniken auf Rechte ist, rief für Spiele für die Datenanalyse für Echtzeit-Datenerfassung für all diese coolen Umgebungen, wo Fast Coat ist wichtig, Also möchten Sie Werden Sie ein C scharfer Performance-Architekt? Möchten Sie dann dem Club beitreten? Das ist der Kurs für Sie. In diesem Kurs werde ich Ihnen eine Zunge von Optimierungs-Hacks beibringen. Wir werden uns die grundlegende Optimierung ansehen. Dies ist also die niedrig hängende Frucht, die einfachen Anpassungen, die Ihren Code bis zu 1000 Mal schneller laufen lassen. Nun, schauen Sie sich die Optimierung der Zwischenprodukte an. Das sind also ziemlich fortgeschrittene Reflexringe aus Ihren Codes. Das wird Ihnen eine kleine Leistungsverbesserung geben. Wir werden uns einige wirklich erweiterte Optimierung ansehen, ist, dass wir Zeiger in C scharf beim Schreiben direkt in den Speicher in einigen Szenarien betrachten werden. Dies ist schneller als die Verwendung der standardmäßigen .net-Klassen. Wir werden uns das Fundament von der Punktnetzlaufzeit anschauen, also werde ich Ihnen einen Crashkurs im Stapel auf dem Heap geben. Wie Wertetypen und Referenztypen von Stores auf dem Stapel, auf dem Heap, wie Daten zwischen dem Stapel auf dem Heap verschoben werden. Wenn wir Mantel laufen, werde ich Sie in die Sprache der Medien lehren, also werde ich tatsächlich Codes Compliance in Zwischensprache übernehmen. Zeigen Sie Ihnen diese Zwischensprache, und ich werde erklären, was alle Anweisungen tun. So am Ende des Kurses, werden Sie in der Lage sein, Zwischensprache zu lesen auf Sie werden sich bewusst, wie dieses Problem Compiler kompiliert. Speichert Mantel in Zwischensprache, wie sicher ich werde Anweisungen können Ihre Küste verlangsamen . Das ist also ein ziemlich großer Kurs. Es enthält viele und viele Vorträge gibt Quiz, um Ihr Wissen auf zu testen. Sie können den Quellcode herunterladen, den ich für Benchmarking verwendet habe. Möchten Sie ein C Sharp Performance Architects werden? Dann ist dies der Kurs für Sie. Also habe ich diesen Kurs für mittlere oder Senioren-Shop-Entwickler erstellt, die lernen wollen, wie man schnell und effizient C scharfen Mantel schreibt, um ihre Karriere für coole Themen wie Spielentwicklungen oder Echtzeit-Datenerfassung vorzubereiten . Danke, dass Sie zugehört haben. Ich freue mich, Sie in diesem Kurs begrüßen zu dürfen. 2. Einführung in die Code-Optimierung: Also lasst uns Code-Optimierung sprechen. Was ist Code-Optimierung? Nun, Zähloptimierung im Allgemeinen impliziert das Ändern des I-T-Systems, um es effizienter zu machen oder weniger Ressourcen zu verbrauchen oder robuster zu sein. Beispielsweise kann ein Computerprogramm so optimiert werden, dass es schneller ausgeführt wird, weniger Speicher oder Festplattenspeicher verbraucht oder reaktionsfähiger in Bezug auf die Benutzeroberfläche ist. Wir optimieren Code, um inakzeptable Verlangsamungen zu vermeiden. Der Gewinnercode wird verwendet. Stellen Sie sich vor, Sie klicken auf eine Schaltfläche Etiketten zeigen Bericht an und müssen dann 10 Minuten warten, bevor etwas auf der akzeptablen Benutzeroberfläche angezeigt wird. Die Verzögerung beträgt zwei Sekunden, also müssen wir einen Weg finden, diesen Bericht in nur zwei Sekunden zu generieren. Wir optimieren auch, um unsere Codes skalierbar zu machen. Ich habe persönlich Websites erlebt, die mit 10 gleichzeitigen Benutzern gut laufen, aber völlig zusammenbrechen, wenn 50 Benutzer die Website alle gleichzeitig besuchen. Es gibt sogar einen Namen dafür. Es wird die Schrägstrich Tochter Effekt Namen für, wenn die beliebten Websites Schrägstrich DOT verfügt über eine Website auf seiner Titelseite, die dann sofort abstürzt, weil Millionen von Besuchern auf den Hyperlink bei Überlastung klicken. Der Web-Server, ein Lied sein Wissen aus Code-Optimierung wird Ihnen helfen, Schleppen diese potenziellen Katastrophen zu vermeiden . Also in diesem Kurs werden wir schauen. So lässt sich ein Programm schneller ausführen. Wir haben eine Reihe von Strategien zur Verfügung. Um dieses Ziel zu erreichen, können wir Algorithmen neu schreiben, um das gleiche Ergebnis zu erzielen. Bei weniger Kälte können wir unnötige Anweisungen vermeiden. Wir können sicherstellen, dass die Bibliotheken, die wir verwenden, speziell für die jeweilige Aufgabe konzipiert sind. Wir können den Speicherverbrauch so weit wie möglich reduzieren, und wir können Szenarien vermeiden, in denen der Code blockiert eine langsame Ressource wartet. Die Leistungsoptimierung in diesem Kurs fällt in eine oder mehrere dieser fünf Kategorien. Aber bevor wir anfangen, ein letztes Wort der Warnung. Es gibt ein berühmtes Zitat von Michael Jackson. Ich meine den britischen Informatiker Michael Jackson, nicht den anderen. Das Zitat sieht so aus. Die erste Regel aus Programmoptimierung ist. Tun Sie es nicht. Der zweite Raum außerhalb der Programmoptimierung, die nur für Experten ist, ist es noch nicht tun. Der berühmte Informatiker Donald's Neuf hatte eine ähnliche Kleidung. vorzeitige Optimierung ist die Wurzel aller Bösen. Das Denken hinter diesen Zitaten ist, dass Ihr Code vollständig zur Hilfe kommt. Wenn Sie versuchen, sich von Anfang an auf die Leistungsoptimierung zu konzentrieren, während Sie noch an Ihrem Programm arbeiten, ist Ihr Mantel Flüssigkeit und entwickelt sich in unerwartete Richtungen. Es ist unmöglich, im Voraus zuverlässig vorherzusagen, wo die Leistungsengpässe liegen werden. Hier ist ein Beispiel am Ende dieses Kurses, Sie werden lernen, dass Zeiger Ihren Mantel beschleunigen können, aber nur, wenn Sie sie auf eine sehr spezifische Weise verwenden. Stellen Sie sich also wieder Faktor in Ihrem gesamten Programm Zeiger zu verwenden, und dann, ein paar Wochen später, müssen Sie auf Ihren Algorithmus und nach unten reflektieren. Der Punkt der Optimierung funktioniert nicht mehr. So, jetzt sind Sie mit unsicheren, schwer zu lesenden Mantel stecken , die tatsächlich langsamer als gut geschrieben, sauber und sicher Fell ist . Das verwendet keine Zeiger, also müssen Sie wahrscheinlich alles rückgängig machen. Ein weiteres Beispiel. Sie haben Wochen damit verbracht, jede mögliche Unze Leistung aus einer Bibliotheksfunktion herauszuquetschen, und schließlich haben Sie es geschafft, die Funktion so zu optimieren, dass sie in weniger als einer Millisekunden ausgeführt wird. Herzlichen Glückwunsch, Dann entwickelt sich Ihr Programm wieder, und wenn Sie in die Produktion gehen, wird Ihre Nachricht 99% der Zeit aufgerufen, direkt nach einem Datenbankkern, der 300 Millisekunden benötigt. Also alle Ihre Optimierung zwart von Nothing. Die empfohlene Vorgehensweise besteht also darin, einfachen, klaren modularen Code auf Verlassen Sie die Organisation bis zum Ende zu schreiben , wenn Sie zuverlässig wissen wo die Leistungsengpässe sein werden. jedoch Es gibtjedochAusnahmen von dieser Regel. Ich überlasse Ihnen die Vollversion von Donalds Nachrichten-Zitat. Wir sollten kleine Effizienzsteigerungen vergessen, sagen etwa 97% der Zeit. Vorzeitige Optimierung ist die Wurzeln des Bösen. Doch wir sollten nicht vergehen, sind Chancen in diesem kritischen 3%. Wenn Sie also die 3% von Anfang an deutlich sehen können, können Sie sofort mit der Optimierung beginnen. 3. Was ist das Stapelspeicher?: Stack-Speicher war einfach der Stack ist ein Block-Off-Speicher, der zum Aufrufen von Methoden und Speichern lokaler Variablen verwendet wird . Lassen Sie mich das hier auf die Tafel zeichnen. Ich werde den Stapel als vertikale Spalte wie folgt zeichnen. Nein, wenn ich mit dem Ausführen von Code beginne, wird dieser Stack zunächst leer sein. Aber wenn Mike Oates die Methoden aufruft, die dies geschieht, die Methodenparameter die Rückgabeadresse für alle lokalen Variablen von den Methoden auf den Stack gesetzt . Dieser ganze Datenblock wird als Stack-Frame bezeichnet. Was passiert also mit dem Stack, wenn meine Codes eine andere Methode innerhalb dieser Methode aufrufen, was dasselbe wieder passiert? Die Methodenparameter geben Adresse für lokale Variablen von der neuen Methode zurück, die ich auf den Stack direkt neben dem vorherigen Stack-Frame setze . Deshalb wird es ein Stack genannt. Die Informationen werden übereinander gestapelt. Was passiert, wenn mein Kult auf return statement trifft? Wie Sie wahrscheinlich wissen, beendet eine return-Anweisung eine Methode und kehrt zum aufrufenden Code zurück. Jetzt auf dem Stapel. Das ist es, was passiert. Der gesamte Stack-Frame, der der Methode entsprach, wird entfernt, aber Sie denken vielleicht, was mit allen lokalen Variablen passiert ist, die im Stack-Frame gespeichert wurden ? Nun, sie gehen alle aus dem Geltungsbereich, was nur eine schicke Art ist, zu sagen, dass sie zerstört sind. Dies ist also eine wichtige Tatsache, um sich an den Moment zu erinnern, in dem Sie alle Ihre lokalen Variablen aus den Methoden zurückkehren , dass Methoden außerhalb des Geltungsbereichs gehen und zerstört werden. Wenn ich mit meinem Programm fortfahre und auch aus den ersten Methoden zurückkehre, sind wir wieder dorthin, wo wir mit einem leeren Stack begonnen haben. Sie fragen sich vielleicht, was passiert, wenn eine Methode andere Methoden aufruft, die eine andere Methode verursachen, die 1000 Mal gut andere Methoden aufruft. Der Stack würde sich schnell mit Stack-Frames füllen, bis normaler Raum vorhanden ist. Schließlich wird der Bestand vollständig auf dem DOT-Netz-Framework Throws-Stack voll sein. Überlauf-Ausnahme. Wenn Sie diese Fehlermeldung sehen, bedeutet dies, dass Sie irgendwo in Ihrem Code eine unendliche Sequenz-Off-Methodenaufrufe haben. Werfen wir einen Blick auf einen Code. Ich habe ein einfaches Programm geschrieben, um ein Quadrat auf dem Bildschirm zu zeichnen. Sie sehen, dass ich eine Schublade quadratische Methoden, die eine Schubladenlinie Methode viermal aufruft, um die vier Seiten von einem Quadrat zu zeichnen. Ich werde eine Pause setzen, Punkte in der Schublade, Linienmethoden und dann meine Kälte laufen. Sieh dir das an. Nun, an diesem Punkt in meinem Mantel, wird dieser Stapel so aussehen. Mein erster Aufruf, Quadrat zu zeichnen, ist hier am unteren Rand des Stacks mit seinen vier Parametern, Rückgabeadresse und lokalen Variablen. Als nächstes kommt der Aufruf in eine Schubladenlinie mit erneut vier Parametern. Rücksendeadresse am. In diesem Fall keine lokalen Variablen, da Schubladenlinie keine in Visual Studio hat. Sie können einen Blick auf diesen Stapel werfen, indem Sie die Call-Stack-Ansicht öffnen, die hier ist. Sie können den Aufruf in ziehen Quadrat und dann in eine Schubladenlinie sehen. Dieses Fenster zeigt Ihnen also an, welche Stack-Frames auf dem Stapel gespeichert sind. Als abschließende Demonstration zeige ich Ihnen einen Stapel. Überlauf-Ausnahme. Lassen Sie mich meinen Mantel so modifizieren. Ich habe meinen Mantel modifiziert. So zeichnen Sie nun eine Linie Anrufe in eine Schubladenlinie, die dann in eine Schubladenlinie ruft, die in eine Schubladenlinie ruft. Du kriegst das Bild. Diese Sequenz wird nie enden. Wenn ich dieses Programm ausführe, wird es eine unendliche Sequenz von gezeichneten Linien Methoden erstellen, die sich selbst nennen Lassen Sie uns das Programm ausführen und sehen, was passiert. Ich bin da, du hast es. Der Stapel ist in der Größe begrenzt. Wenn ich zu viele Methoden habe, die alle Methoden aufrufen, wird der Stack schließlich vollständig auf dem DOT-Net-Framework die Stack-Überlaufausnahme voll sein. Also, was haben wir gelernt? Das wird Net Framework verwendet den Stack, um Medikamente jedes Mal zu verfolgen, wenn Sie die Nachricht alle Methodenparameter aufrufen . Die Rückgabeadresse für alle lokalen Variablen wird auf dem Stapel platziert. Dieser gesamte Speicherblock wird als Stack-Frame bezeichnet. Wenn Sie aus einer Methode zurückkehren, der oberste Stack-Frame entfernt. Alle lokalen Variablen gehen an dieser Stelle aus dem Geltungsbereich, und ich habe zerstört. Wenn Sie eine unendliche Sequenz Off-Methoden haben, wird der Aufruf aller Methoden, die der Bestand vollständig gefüllt, bis Brennnessel einen Stapel wirft. Überlauf-Ausnahme 4. Was ist das heap: die andere Art von Speicher in einem Computer wird als Trefferspeicher oder einfach der Heap bezeichnet. Werfen wir einen Blick auf die folgende Codezeile jetzt. Jedes Mal, wenn die Schlüsselwörter new online angezeigt werden, haben Sie ein Objekt auf dem Heap erstellt. Das ist eine Grundregel. Internetobjekte werden immer auf dem Heap erstellt. Sie gehen nie auf den Stapel. Also habe ich einige Änderungen an meinem Schubladen-Quadrat-Programm vorgenommen. Werfen wir einen Blick. Zuvor erwarteten die Methoden der Schubladenlinie vier ganzzahlige Parameter, um eine Linie zu zeichnen. Aber jetzt habe ich eine Schublade Polygon Methods, die ein Array Offline-Objekte erwartet, das alles auf einmal zeichnet. Die Draw square Methode richtet ein Linienfeld mit vier Objekten, die den vier Seiten aus dem Quadrat entsprechen , und ruft dann alle eine Waffe fallen lassen, um alles in einem zu zeichnen. Gehen Sie jetzt denken Sie daran, in meinem alten Schubladen-Quadrat-Programm, Ich legte einen Bruchpunkt in die Schubladenlinie Methoden und wenn ich meine Codes lief, sah der Stapel so aus. Aber jetzt habe ich viele Änderungen am Programm vorgenommen. Also, wie wird der Stapel auf dem Heap jetzt aussehen? Okay, also stell dir vor, ich habe einen Bruchpunkt in die Schublade gelegt. Polygon-Methoden beim Ausführen meines Programms. Der Stapel auf dem Heap würde dann so aussehen. Die Parameter kalten Linien existieren auf dem Stapel, weil es ein Parameter ist, aber ich habe es mit den neuen Schlüsselwörtern initialisiert. Das Array selbst wird also auf dem Heap erstellt, so dass die Variable auf diesem Stack auf ein Objekt auf dem Heap verweist. Sie können auch sehen, dass das Linien-Array vier Elemente hat und dass jedes Element sich auf ein Linienobjekt an anderer Stelle im Jetzt bezieht , wenn Sie denken, dass alles viel komplizierter aussieht als der vorherige Kult, dann sind Sie unter dem rechten Spur auf. Das bringt mich zu einem wichtigen Punkt. Dieser Code, der ein Weingut verwendet, ist etwas langsamer als mein vorheriger Mantel, der ganze Zahlen für alles verwendet. Und das liegt daran, dass alle zusätzlichen Referenzen berechnet werden müssen, vom Linien-Parameter auf dem Stapel bis zum Array. Instanz wird auf dem Heap von einem Array-Element des Heaps an eine Line-Objektinstanz an einer anderen Stelle auf dem Heap übergeben. Also das Haupt wegnehmen, denn jetzt sind Codes, die ganze Zahlen verwenden, etwas schneller als Code, der Objekte verwendet. Also, was passiert, wenn die Polygon-Methode zum Zeichnen beendet ist. Dieser Stapelrahmen wird auf den Lichtern entfernt, Parameter geht aus dem Bereich und wird zerstört. Aber hier ist etwas, das Sie vielleicht nicht erwartet haben. Z-Array auf der Linie. Objekte auf dem Heap sind jetzt weiterhin vorhanden. Das ist eine interessante Situation. Der Löwenparameter ist außerhalb des Geltungsbereichs, aber die Objekte auf der Hitze sind immer noch da. Wir sagen, dass die Objekte aufgrund der Variablen oder des Parameters de referenziert werden, die sich auf sie ohne Umfang beziehen. De referenzierte Objekte existieren weiterhin und werden nicht sofort zerstört. Also hier ist ein weiterer wichtiger Take away. Das DOT-Netz-Framework wird immer verschoben. Bereinigen der referenzierten Objekte liegt daran, dass das Bereinigen des Heaps viel Zeit in Anspruch nimmt , indem es so lange wie möglich verschoben wird. Ihr Code wird tatsächlich schneller ausgeführt, aber schließlich muss das Framework den Heap säubern, sonst würde uns schnell der Speicher ausgehen . Dieser Reinigungsvorgang wird als Garbage Collection bezeichnet und geschieht regelmäßig im Hintergrund. Wenn das Framework Garbage Collecting startet. Es identifiziert alle Objekte auf dem Heap, die nicht mehr durch einen variablen Parameter oder Objekte in Ihrem Mantel referenziert werden, auf dessen De weist jedem von ihnen zu. Wir werden in späteren Vorträgen zur Garbage Collection zurückkehren, und ich werde Ihnen einige Tipps geben, wie Sie Leistungsprobleme in Ihren Codes aufgrund der Garbage Collection vermeiden können. Also, was haben wir gelernt? Jedes Mal, wenn Sie die neuen Schlüsselwörter in Ihren Codes verwendet haben, erstellen Sie ein Objekt auf dem Heap. Die Variable selbst kann auf dem Stapel leben, aber sie bezieht sich auf ein Objekt auf dem Heap. Das neue Zeichenquadrat Programm, das ein Linienfeld verwendet, ist etwas langsamer als das alte Programm , das ganze Zahlen verwendet. Der Grund dafür ist, dass alle zusätzlichen Objektreferenzen verarbeitet werden müssen, wenn Parameter und lokale Variablen auf dem Stack außerhalb des Gültigkeitsbereichs liegen. Die entsprechenden Objekte aus dem Heap werden nicht zerstört. Sie existieren weiterhin in einem D-referenzierten Zustand. Das nächste Framework verschiebt die Bereinigung der referenzierten Objekte auf dem Heap aus Leistungsgründen so lange wie möglich. Aber schließlich startet das Framework einen Prozess namens Garbage Collection. Da de alle referenzierten Objekte auf der Hüfte zuweist 5. Was sind Werttypen?: in der vorherigen Vorlesung haben wir über diesen Stapel auf der Hitze erfahren. Das Wissen, das Sie gewinnen, wird Ihnen helfen, vorwärts zu gehen, wenn wir uns Variablen ansehen, wie sie im Speicher durch das Punktnetz-Framework gespeichert werden und wie sich dies auf die Leistung Ihres Codes auswirkt . Zuvor, als ich über diesen Stapel sprach, zeigte ich Code mit der Schublade. Linus. Es ist, dass vier ganzzahlige Parameter verwendet. Werfen wir einen genaueren Blick auf eine ganze Zahl im Dark Net-Framework. Der aufständische Typ ist Teil einer speziellen Klasse von Typen, die Werttypen genannt werden, aber was ist der Werttyp? Der Werttyp ist ein Variablentyp, bei dem der Typ des Wertes aus der Variablen zusammen gespeichert wird. Wenn ich also eine lokale Integer-Variable mit einem Wert von 12 Hunderten und 34 habe, wird dieser Interview-Typ auf seinem Wert zusammen so gespeichert. Werfen wir einen Blick auf diesen Stapel noch einmal. Zuvor, als ich über diesen Stack sprach, erwähnte ich alle Arten von Daten, die auf dem Stapel gespeichert sind, Nachrichtenparameter sind. Die Rücksendeadresse aus einer Nachricht ertönt lokale Variablen. Wenn ich also eine lokale Variable vom Typ Integer mit dem Wert von 12 Hunderten und 34 habe, würde sie so auf dem Stack gespeichert werden. Siehst du das? Der Typ und der Wert, der zusammen auf dem Stapel gespeichert ist. Denken Sie nun daran, denn in der nächsten Vorlesung werde ich über Referenztypen sprechen, die Speicherung anders ist. Sie fragen sich also vielleicht, welche Typen im dunklen Net-Framework tatsächlich Werttypen sind. Nun, hier ist eine vollständige Liste. Alle numerischen Typen sind Werttypen, einschließlich aller Ganzzahl-Gleitkommastellen auf Dezimaltypen. auch Boolescher Wert in Operationen und Strukturen sindauchWerttypen. Alles andere im Punktnetz-Framework wird als Referenztyp bezeichnet , den ich in Kürze besprechen werde. Wenn Benutzer versuchen, den Unterschied zwischen einem Werttyp und einem Referenztyp zu erklären, hören Sie oft die folgende Erklärung. Ein Werttyp ist ein Typ, der auf dem Stapel vorhanden ist. jedoch Dies istjedochRaumwert. Typen können sowohl auf dem Stapel auf dem hier existieren, lassen Sie mich demonstrieren. Zuvor, als ich über den Heap sprach, erwähnte ich, welche Art von Daten auf dem Heap gespeichert sind. Erinnern Sie sich, was es waas? Es waren alle Objekte, Instanzen, die mit einem neuen Schlüsselwort in c scharf erstellt wurden. Stellen Sie sich also vor, dass ich ein Objekt auf dem Heap mit den neuen Schlüsselwörtern in meinem Mantel auf diesem Objekt erstelle, hat ein Feld mit dem Wert 12 Hunderte beigesetzt. 34. Jetzt wird diese ganze Zahl so gespeichert. Sie sehen also, ich weiß, dass ich den Werttyp auf dem Heap habe, so dass Werttypen sowohl auf dem Stapel am Tag existieren können die definierenden Eigenschaftswerttypen nicht dort sind, wo sie gespeichert werden, sondern dass der Wert zusammen mit dem Typ gespeichert wird. Lassen Sie mich zum Abschluss bringen, indem Sie zusätzliche Features oder Werttypen Wichtigkeit zeigen. Nehmen wir an, ich habe eine Nachricht in meinen Codes mit zwei Variablen A und B. Beide sind eingeführt. Die Variable A enthält den Wert 1234 unter Variable B ist Null. Was passiert, wenn ich es zuweise, wird Check this out sein. Der Wert von A wird in be kopiert. Nun ist dies ein wichtiges Merkmal der Wertetypen ihre Vorzeichen nach Wert, was bedeutet, dass ihr Wert kopiert wird. Also, was passiert jetzt, wenn ich A und B vergleiche? Sie sind zwei verschiedene Variablen, die nur zufällig den gleichen Wert enthalten. Also, wie wird es. Das Punktnetz-Framework interpretiert die Situation gut so. Das Framework betrachtet diese beiden Variablen als gleich. Dies ist die zweite wichtige Funktion aus Werttypen. Sie werden nach Wert verglichen, was bedeutet, dass zwei verschiedene Variablen, die den gleichen Wert enthalten, als gleich angesehen werden. Also, was haben wir gelernt? Werttypen speichern ihren Wert direkt, zusammen mit den Typwerttypen können auf dem Stapel vorhanden sein und ein Schaltwerttypen sind ein Zeichen nach Wert, was bedeutet, dass der Wert über Wert kopiert wird. Typen werden nach Wert verglichen. Zwei Variablen mit demselben Wert werden als gleich angesehen. 6. Was sind Referenztypen?: In der vorherigen Vorlesung sprach ich über die Wertarten und erwähnte kurz sein Gegenstück den Referenztyp. Aber was ist ein Referenztyp? Nun, ein Referenztyp ist ein Variablentyp, der sich auf einen Wert bezieht, der auf dem Heap gespeichert ist. Vorher nicht. Als ich über den Heap sprach, zeigte ich mein modifiziertes Zeichenquadrat Programm, das eine Polygon-Methode zeichnen hatte, wenn Sie sich mit einem Linienarray-Parameter erinnern, So zeichnen Polygon ein Array Offline-Objekte erwartet. Werfen wir einen genaueren Blick auf diese Linienobjekte, nur um Ihren Speicher zu aktualisieren. Hier ist wieder mein Mantel. Sie können die Definition außerhalb der Line-Klasse hier sehen. Es ist ein einfacher Datencontainer mit zwei Sätzen von Koordinaten. Stellen Sie sich also vor, ich habe eine Nachricht mit einer lokalen Variablen außerhalb der Typzeile. Wie würde das aussehen? Ein Gedächtnisbrunnen, wie dieser. Sie können sehen, dass sich die Variable selbst auf dem Stapel befindet, aber sie bezieht sich auf eine Zeile Objekte auf dem, aber kann eine Referenztypvariable auch auf dem Heap existieren ? Ja, sicher. Alles, was ich tun muss, ist ein Objekt auf dem Heap zu erstellen. Die Verwendung der neuen Schlüsselwörter hat die Hälfte gedreht, dass Objekte ein Feld außerhalb der Typzeile haben. Nun wird die Erinnerung dann so aussehen. Sie sehen, dass sie jetzt eine Referenztypvariable auf dem Heap haben, und es bezieht sich auf eine Linie Objekte, die auch auf dem was an anderer Stelle gespeichert ist, so zusammenzufassen. Referenztypen können auf dem Stapel am Tag auf dem Heap vorhanden sein, genau wie Werttypen, aber sie beziehen sich immer auf einen Wert auf dem Heap. Lassen Sie mich abschließend zeigen, dass Sie zusätzliche Funktionen von Referenztypen wichtig sind. Nehmen wir an, ich habe eine Nachricht in meinen Codes mit zwei Variablen A und B. Beide Variablen sind Zeilen. Die Variable A bezieht sich auf eine Zeileninstanz auf dem Heap unter Variable B wird gesagt, um zu wissen. Nun, was passiert, wenn ich A zu B. überprüfe, wird die Referenz von A in sein kopiert. Dies ist ein wichtiges Merkmal von Referenztypen. Sie sind Zuordnungen durch Referenz, was bedeutet, dass die Referenz kopiert wird. Sie enden mit zwei Variablen, auf die gleiche Objektinstanz in der Hüfte beziehen. Also, was passiert jetzt, wenn ich A und B vergleiche? Es handelt sich um zwei verschiedene Variablen, die sich auf dieselben Objekte auf dem Heap beziehen. Wie wird das Punktnetz-Framework diese Situation gut so interpretieren? Das Framework betrachtet diese beiden Variablen als gleich. Aber warten Sie, was ist mit diesem Szenario, um Typvariablen zu referenzieren, die auf zwei separate Objekte auf dem Heap verweisen . Beide Objekte enthielten jedoch identische Daten. Wie wird das Punktnetz-Framework diese Situation interpretieren, die Drinker klingt. Das Framework betrachtet diese beiden Variablen als nicht gleich, so dass dies ein weiteres wichtiges Merkmal von Referenztypen ist. Sie werden durch Referenz verglichen, was bedeutet, dass zwei verschiedene Variablen, die sich auf die gleichen Objekte beziehen, als gleich angesehen werden. Aber zwei verschiedene Variablen, auf zwei separate, aber identische Objekte beziehen, gelten als ungleich. Also, was haben wir gelernt? Referenzzeiten können auf den No-Value-Speicher Reference Types erkannt werden, ein Verweis auf ihren Wert, und dieser Wert wird immer auf der Referenz gespeichert. Typen können auf diesem Stapel am Tag am vorhanden sein, aber ihr Wert wird immer in der Referenz gespeichert. Typen sind Zuweisungen durch Referenz, was bedeutet, dass die Referenz über Referenz kopiert wird. Typen werden als Referenz verglichen. Zwei Variablen, die sich auf dieselben Objekte beziehen, werden als gleich angesehen, und zwei Variablen, die sich auf separate, aber identische Objekte beziehen, werden als ungleich betrachtet 7. Was ist das Boxen und das Unboxing?: In diesem Vortrag werde ich Ihnen ein Rätsel zeigen. Werfen Sie einen Blick auf diesen Code. Dies ist ein sehr einfaches Programm. Ich habe mit der Variablen A begonnen, die den Wert 1234 enthält. Dann deklariere ich eine zweite Variable. Nicht Typ Objekte sein. Andi. Ich habe B in C. Sharp zugewiesen . Alle Typen erben von Objekten, einschließlich Ganzzahlen, so dass Sie grundsätzlich alles in eine Objekttypvariable einfügen können. Aber warten in Juroren sind Werttypen, und Objekte sind referenzierte Typen. Also im Speicher werden meine Variablen so gespeichert. Hier ist meine ganzzahlige Variable A mit seinem Wert von 1234. Und hier ist meine Objektvariable B. Aber B ist ein Referenztyp, und wir haben in der vorherigen Vorlesung gelernt, dass Referenztypen hier immer auf einen Wert auf dem Heap verweisen . Dies ist nicht möglich, da A eine lokale Variable ist, daher existiert sie auf dem Stapel, der gefunden hat, dass es sich um einen Werttyp handelt, daher wird ihr Wert auch auf dem Lager gestartet. Es gibt einfach keine Möglichkeit für B, sich auf a zu beziehen, da beide Variablen in verschiedenen Speichertypen rezitiert . Ein Referenztyp kann sich nie auf den Stack-Speicher beziehen, so dass dieses Programm nie funktionieren kann. Richtig? Nun, das hier tut es. Ich gehe zurück zum Dezember im Studio Andi, führe das Programm. Los geht's. Cool. Es funktioniert tatsächlich. Aber wie ist das möglich? Nun, das ist komisch. Basierend auf dem, was wir in früheren Vorlesungen gelernt haben, sollte dieses Programm nicht funktionieren. Aber doch tut es das. Wie kann man das herausfinden? Lassen Sie mich dieses Programm in die Zwischensprache zwingen. Indem wir die Zwischensprachenanweisungen untersuchen, könnten wir einen Hinweis finden. Und hier ist es. Sieh dir das an. Fortgeschrittene Sprachunterricht genannt Bücher. Kannst du erraten, was es hier macht? Ich ziehe an die Tafel. Hier sind die Speicherlayouts mit den beiden Variablen A und B. Nun tut die Buchanweisung dies. Damit das Programm funktioniert, kopiert das Net-Framework tatsächlich den Interviewer-Wert aus dem Stapel besteuert dann den Wert in Objekte darauf Platziert diese Objekte auf dem, so dass die Variable B dann darauf verweisen kann . Dieser ganze Prozess wird Boxen genannt. Boxen findet jedes Mal hinter den Kulissen statt. Wenn Sie eine Variable Parameter Felder oder Eigenschaft off Zeit Objekte und Sie einen Werttyp zu seinem Boxen zuweisen ist schön, weil Es Art der Unschärfe der Linie zwischen Wertzeiten und Referenztypen. Aber Boxen kann auch ein Schmerz sein, weil es zusätzliche Gemeinkosten in Ihrem Code einführt. Jetzt fragen Sie sich vielleicht, ob es einen entsprechenden Prozess namens Unboxing gibt. Ja, das gibt es. Hier ist wieder mein Programm. Werfen wir einen Blick auf die letzte Zeile. Ich deklariere eine Variable, siehe Typ Integer und weisen den Objektwert mit einem Typecast zu. Noch ein bisschen Magie. Eigentlich, weil siehe, existiert auf dem Stapel und sein die Objekte bezieht sich auf ein Objekt auf die in der Zwischensprache . Die entsprechende Anweisung ist hier. Es heißt Unbox. Lassen Sie mich zurück zur Tafel auf Draw the Unboxing Prozess. Wir begannen von der Box-Situation mit der Ganzzahl auf der Now. Dann passiert das. Unboxing entpackt die ganze Zahl auf dem Heap und kopiert den Wert zurück in die Variable Siehe auf dem Stapel. Entpacken erfolgt jedes Mal hinter den Kulissen, wenn Sie einen Objektwert haben und ihn in einen Werttyp umwandeln . Boxen und Unboxen können die Leistung Ihres Mantels ernsthaft beeinträchtigen, also stellen Sie sicher, dass Sie es in geschäftskritischen Abschnitten so weit wie möglich vermeiden. Ich werde einige Tipps und Tricks in späteren Vorträgen, wie dies zu tun ist, teilen. Was haben wir gelernt? Boxing ist der Prozess, Werttypen auf dem Stapel zu nehmen und sie in Objekte zu packen, um diese Objekte auf dem Heap zu platzieren. Boxing geschieht, wenn Sie einen Werttyp einem Variablenparameter von Feld- oder Eigenschaftenobjekten zuweisen . Unboxing ist der umgekehrte Prozess. Objekte aus dem Heap werden für den Wert entpackt. Typen innerhalb werden zurück in den Stapel kopiert. Lernen Boxen passiert immer dann, wenn Sie keinen Objektwert haben und Sie ihn auf einen Werttyp werfen. Boxen und Unboxen haben sich negativ auf die Leistung Ihres Mantels ausgewirkt und sollten in geschäftskritischen Abschnitten vermieden werden . 8. Was sind unveränderliche Zeichenketten?: in dieser Vorlesung. Wir werden jetzt einen Blick auf die String-Klasse in Punktnetz werfen. Was denken Sie, ist eine Zeichenfolge vom Werttyp oder ein Referenztyp? Lass es uns herausfinden. Ich habe Codes durchgeschrieben, um zu testen, was eine Zeichenfolge tatsächlich ist. Sieh es dir an. Ich beginne mit der Deklaration der String-Variable a beim Initialisieren Es ist auf den Wert ABC. Dann deklariere ich eine zweite String-Variable B und ein Zeichen A zu B. In der nächsten Zeile füge ich ein einzelnes Zeichen am Ende des Streams hinzu und schließlich schreibe ich beide Strings in die Konsole. nun Wenn Strings referenzierte Typen sind, würden der Stack und der Heapnunso aussehen. Ich müsste die Variablen A und B, beide auf die gleichen String-Objekte auf dem Heap zeigen. dann Wenn ichdanndie String-Variable B ändere, würde die Zeichenfolge invariable A auch geändert werden, da beide Variablen auf dieselbe Zeichenfolge verweisen . jedoch Wenn ZeichenfolgenjedochWerttypen sind, würde der zweite Heap so aussehen. Ich müsste Variablen a und B, die separate Zeichenfolgen enthalten, wenn ich String-Variable Bändere B Die andere Zeichenkette invariable A ist nicht betroffen. Welches Szenario stimmt. Denken Sie, Lassen Sie uns herausfinden, indem Sie meinen Code ausführen. Hier gehen wir und da hast du es. Strings sind offensichtlich Werttypen, oder? Nun, keine Strings sind tatsächlich referenzierte Typen im Speicher. Dinge sehen so aus. Hier sind die beiden Variablen A und B, sich beide auf die gleiche Zeichenfolge auf dem Heap beziehen. Aber etwas Besonderes passiert, wenn ich die String-Variable B ändere, anstatt die Zeichenfolge auf dem Heap direkt zu modifizieren . Das passiert. Wir sagen, dass Strings unveränderlich sind. Objekte im Netz. Jede Änderung an einer Zeichenfolge führt dazu, dass eine neue Zeichenfolge im ursprünglichen Stream erstellt wird, bleibt unberührt. diesem Grund verhalten sich Strings so, als wären sie Werttypen. Also, was sind die Vorteile von unveränderlichen Strings Bedrohung Sicherheit, unveränderliche Strings? Eine Bedrohung sicher? Weil sie nicht geändert werden können Speichereinsparungen. Identische Saiten können sicher miteinander verschmolzen werden. Dies wird beim Drehen der ersten Zuweisung aufgerufen, um eine Zeichenfolge zu kopieren. Alles, was Sie tun müssen, ist die Referenz zu kopieren, anstatt alle Zeichen nacheinander kopieren zu müssen. Und erste Vergleichszeichenfolgen können verglichen werden, indem die Referenz verglichen wird , anstatt alle Zeichen nacheinander vergleichen zu müssen. Werfen wir einen Blick auf die Zwischencodes, um zu sehen, was hinter den Kulissen vor sich geht. Ich setze einen Bruchpunkt auf die letzte Zeile von meinem Programm. Starten Sie das Programm und wechseln Sie dann zu Zwischencodes. Die erste Zeile ist hier. Eine Zeichenfolge mit Inhalt. ABC wird auf die geladen mit der Load-String-Anweisung auf speichert unveränderliche A mit einer Filialstandort-Anweisung . Zen A wird zugewiesen, indem einfach die Referenz unveränderliche A mit einer Ladeposition geladen wenn sie in die Variable B mit einem Shopstandort gespeichert wird, eine superschnelle String-Zuweisung durch Referenz genau das, was wir für einen unveränderlichen Typ erwarten würden . Die Magie passiert, wenn ich die Zeichenfolge unveränderlich hinter den Kulissen ändere. Das Framework ruft eine String-konzussierte Methode auf. Lassen Sie mich diese Methode mit dem Assembly-Browser im Summering Studio nachschlagen. Hier ist es. Sie können sehen, dass die Dose Katze Chaos es ist schafft eine neue Zeichenfolge hier. Es kopiert dann beide String-Argumente in die neue Zeichenfolge und gibt hier einen Verweis auf die neue Zeichenfolge zurück. Also da hast du es. Stattdessen erstellt das Ändern dieser Zeichenfolge unveränderlich die Codes einen völlig neuen Stream, kopiert alles in seinen und speichert einen Verweis auf die neue Zeichenfolge. Unvariable Be die alte Zeichenfolge in B bleibt auf dem Heap in einem Deal referenzierten Zustand, der darauf wartet, Garbage gesammelt zu werden. Jede Methode in der String-Klasse, die die Zeichenfolge in irgendeiner Weise ändert, hat dieses Verhalten. Stattdessen deaktivieren Sie direkt den Stream zu ändern. Es erstellt eine ganze neue Zeichenfolge und platziert die Änderungen. Da drinnen. Die ursprüngliche Saite bleibt unberührt. Also, was haben wir gelernt? Zeichenfolgen sind referenzierte Typen auf und unveränderlich. diesem Grund verhalten sich Strings so, als wären sie Werttypen. Sie sind Zeichen und vergleicht nach Wert. Unveränderliche Strings sind Bedrohungen sicher in musikalischen Streichern sind schnell, weil sie signiert und verglichen werden können durch Referenz. Unveränderliche Strings sparen Speicher, da identische Strings im Speicher zusammengeführt werden können. 9. Ein Crashkurs in Zwischensprache in intermediate: in diesem Vortrag werde ich über Zwischensprache sprechen. Also, was genau ist Zwischensprache? Nun, lassen Sie mich damit beginnen, Ihnen einen klassischen Compiler zu zeigen, in diesem Fall einen C oder C plus, plus Compiler. An der Spitze. den Tafeln Vonden Tafelnsehen Sie ein einfaches Stück Code, eine Vier-Schleife, die eine ganze Zahl zu einem variablen Kaltergebnis hinzufügt. Ein, C oder C plus plus Compiler würde diese Fragmente von Source Coat nehmen und es in die Maschinensprache kompilieren , was im Grunde nur eine Reihe von Zahlen ist, die Speicher speichern, auf der von der CPU in Ihrem Computer ausgeführt wird , aber ein Darknet -Compiler. Stäube Dinge anders. Werfen wir einen Blick auf das gleiche Stück Codes geschrieben in C. Sharp. Ein C scharfer Compiler wird zuerst die Quellküste zu einer speziellen Zwischensprache namens Well kompilieren , Sie haben diese gemeinsame Zwischensprache erraten. C i, l oder Seide. Der gleiche Mantel wird in einer DLL- oder Xer-Datei gespeichert. Wenn der Code ausgeführt wird, tritt ein anderer Compiler ein. Dieser Compiler wird als Jit-Compiler bezeichnet, oder G i T G. I T steht für just in time. Der Compiler läuft gerade rechtzeitig zu den letzten möglichen Momenten, bevor die Codes falsch sein müssen . Der Jit-Compiler nimmt die Zwischensprache und kompiliert sie die Maschinensprache. Warum also dieser komplizierte zweistufige Kompilierungsprozess? Nun, weil es tatsächlich eine Reihe von wichtigen Vorteilen hat. Erstens kann der Seidenmantel für die Plattform optimiert werden, auf der er läuft. So Unterschiede zwischen, zum Beispiel, ein M D auf Intel. Die CPU-Nutzung kann vom Jit-Compiler vollständig ausgenutzt werden, um eine schnelle und optimierte Maschinensprache zu erstellen . Sekunden. Die bald kalt ist voll portabel, da sie nicht an eine bestimmte Hardware-Plattform gebunden ist. Der Mantel kann unter Windows auf Lenox auf Apple-Computern ausgeführt werden. In der Tat habe ich diesen ganzen Kurs auf einem Apple MacBook Pro und allen Codebeispielen erstellt. Sie werden eine läuft nativ auf OS X sehen, aber Sie könnten genau diese gleichen ausgeführten Bols nehmen, die ich laufe, kopieren Sie sie zu schiefen oder Windows und führen Sie sie dort. Es würde funktionieren. Ein weiterer nützlicher Vorteil aus Seide Mantel ist, dass es sehr Kampf vor dem Laufen, um sicherzustellen , dass der Mantel keine gefährlichen Operationen und schließlich, noch kalt kann mit Metadaten kommentiert werden, zum Beispiel, Serialisierungsanweisungen, die externe Kälte genau sagen, wie Sie Ihre Klassen XML und zurück konvertieren . Gibt es irgendwelche Nachteile bei der Verwendung von Seidenmantel? Nun, ja. Verwendung einer zweistufigen Kopulation ist etwas weniger effizient als das direkte Kompilieren von Quellcodes zur Maschinensprache. So kompiliert. Die Netto-Erkältungen sind etwas langsamer als direkt kompilierte C oder C plus Code-Hintern. Compiler haben ihn in den letzten Jahren wirklich gut verursacht, und so ist dieser Unterschied mikroskopisch klein geworden. Heute ist es fast unmöglich, Kult-Maschinensprache zu handhaben, die effizienter ist als was der C Sharp Compiler produziert. Schauen wir uns also die Zwischensprache genauer an. Wie funktioniert die Sprache eigentlich? Intermediate Sprache basiert auf drei Konzerten. Die erste ist die Reihenfolge aus Anweisungen auf den Schreibvorgängen auf der Tafel gezeigt. Intermediate Language Programme enthalten, oder die Sets Off Anweisungen, die in der Reihenfolge ausgeführt werden, genau wie ein C scharfes Programm. Die lokalen Variablen in einem Zwischensprachenprogramm, ein in speziellen Slots gespeichert genannt Standorte. Es gibt eine Reihe von Standorten zur Verfügung. Ich habe vier auf der Tafel auf der linken Seite gezeichnet. Schließlich gibt es eine Auswertung Stack ein Stack ist eine Sammlung von Werten mit zwei grundlegenden Operationen, einem Push namens Load in Ill, der dem Stapel Wert hinzufügt und alles andere um eine Ebene nach unten schiebt, die andere Operation ist ein Pop namens Store in Ill, der den obersten Wert aus dem Stapel entfernt, wenn voll bewegt wird. Andere Werte nach oben auf einer Ebene. Es gibt drei Gruppen von Anweisungen in Zwischensprachenanweisungen, die Daten über die Auswertung weitergegeben haben . Stecken Anweisungen, die Operationen auf dem Stapel auf Anweisungen ausführen, die Werte aus dem Stapel Pop-Werte . Also schauen wir uns mal an. Das ist ein sehr einfaches Programm. Werfen Sie einen Blick auf die folgenden zwei Codezeilen, die ich mit dem Wert 456 eine ganze Zahl initialisiert und dann eine zu diesem Wert hinzufügt. Wie würde dieses Programm in der Zwischensprache aussehen? Nun, dieses Programm bestand aus nur vier Zwischensprachenanweisungen. Die erste Anweisung ist lastenkonstant. Eins, die die vier Bytes Integer-Wert mit Vorzeichen lädt. Einer auf der Auswertung stecken Note, dass es nur eine lokale Variable in meinem Programm namens I und so wird es in Position Null gespeichert, mit dem Anfangswert von 456. Die nächste Anweisung ist, lädt Location Null, die den Wert aus der Variablen I in Position Null auf der Auswertung stecken lädt. Also jetzt haben wir zwei Nummern der Aktien eins und 456. Die dritte Anweisung ist Tante, die die beiden oberen Zahlen auf dem Stapel zusammen addiert. Das Ergebnis ist also 457, die auf den Stapel geht und die ursprünglichen zwei Zahlen ersetzt. Also, jetzt haben wir eine einzelne Zahl auf dem Stapel. 457. Der letzte Anfang ist Store Location Null, der den Spitzenwert aus dem Auswertungsbestand springt und ihn in Location Null speichert, was der Variablen I entspricht. Hier sind ein paar weitere Anweisungen, die Sie treffen können, wenn Sie schauen auf kompilierten C scharfen Mantel. Die Bücher und Anweisungen zum Auspacken machen genau das, was Sie erwarten könnten. Sie Box und dann Bücher Werttypen auf der B und E Anweisung steht für Zweig, nicht gleich. Es springt zu einem Unterschied Location Mantel. Wenn die beiden oberen Zahlen auf dem Auswertungsbestand nicht gleich, cool und virtuell genannt sind , rufen Sie statisch auf nicht statischen Klassenmitgliedern auf, laden Sie Elemente und speichern Sie Elemente in einem dimensionalen Rennen. Neues Array als neue Objekte erstellt jeweils ein neues Array . Die neuen Objekte auf der Rückgabe gibt von einer Methode zurück und wirft eine Ausnahme aus. Also haben Sie vielleicht gedacht, dass die Zwischensprache sehr komplex ist, aber es ist eigentlich ziemlich einfach. Die Sprache ist nicht so schwer zu verstehen, und in den kommenden Vorträgen waren dabei, einen Blick auf Kompilierung zu nehmen, siehe Shark Geruch viel und analysieren, wie der Compiler übersetzt sie scharfen Quellcode in Zwischensprache, was die Auswirkungen auf die Leistung sind. Werfen wir einen Blick auf das einfache Programm. Werfen Sie einen Blick auf meinen Code hier. Ich beginne mit der Nummer zwei und benutze dann diese vier Schleife, um die 1. 16 Potenzen von zwei zu berechnen , indem ich die Zahl wiederholt mit zwei multipliziere. Wie sieht das in der Zwischensprache aus? Nun, alles, was ich tun muss, ist, am Ende des Programms hier einen Bruchpunkt zu setzen, mein Programm auszuführen und dann zur Disassemblierung zu wechseln. Wir betrachten jetzt die Kompilierung Intermediate Language Mantel mit Anmerkungen aus dem ursprünglichen Quellcode, so dass wir leicht die kompilierten Fohlen für eine bestimmte Zeile von C scharfen Codes finden konnten . Beginnen wir also mit der Deklaration aus der Zahlenvariablen. Hier sehen Sie eine Lastkonstante zu Anweisung, die die Nummer zwei auf den Stapel und dann einen Ladenstandort Null schiebt, der die Zahl in Position Null speichert. Wir wissen also, dass die Position Null der Zahlenvariablen entspricht. Dann kommt das Forum. Achten Sie nun genau auf die Adressetiketten hier, da die Codes nicht in Ordnung angezeigt sie leichter lesbar zu machen. Der erste Schritt besteht darin, die Variable I zu initialisieren 20 So haben wir eine Lastkonstante Null auf und speichern Ort eine Anweisungen hier. Jetzt wissen wir, dass die Variable I in Position 1 gespeichert ist. Dann springt der Kult zu Ort 14. Dort haben wir einen Ladeort eins , der sehr gut ist. Ich lade Constant 16, die die Nummer 16 lädt, dann eine BLT-Anweisung. Jetzt steht BLT für Zweig weniger als auf. Es wird zu Position A springen , wenn die Variable I kleiner als 16 ist. Position A enthält den Hauptkörper außerhalb der Schleife. Es lädt die Zahlenvariable auf den Stapel. Dann lädt es die Nummer zwei auf den Stapel. Es multipliziert beide Zahlen auf dem Stapel zusammen und speichert die Ergebnisse zurück in die Zahlenvariable, da der Code nicht in Ordnung ist. Die nächste Anweisung nach Standort F ist eigentlich hier bei Standort 10. Wir sind wieder da, wo die Variable I Inkrement ID getestet hat. Wenn es unter 16 ist, war die Variable. Ich erreiche den Wert 16. Wir werden hier die BLT-Anweisung durchlaufen. Der nächste Standort nach Standort 17 ist eins. Siehe auf der Rückgabe-Anweisung gibt aus der Methode zurück, und da haben Sie es. Ein fünfzeiliges C scharfes Programm kompiliert nur 17 Zwischensprachenanweisungen auf die kompilierte Kälte war ziemlich einfach zu lesen, also Glückwünsche sind Sie jetzt in der Lage, kompiliert zu lesen. Siehe Haifischmantel. der Lage zu sein, Zwischensprachcodes zu lesen und zu interpretieren, ist eine sehr wichtige Fähigkeit, die Ihnen sehr helfen wird, wenn Sie die Leistung von Ihrem Mantel zu optimieren. Also, was haben wir gelernt? C. Sharp Coats kompiliert zu Intermediate Language Coat , der dann von einem Just in-Time-Compiler wieder kompiliert wird. Zwei Maschinensprache Gits Copulation kann Codes für lokale Hardware in Medien-Sprachcodes optimieren , ist portabel und kann auf mehreren Plattformen wie Windows, Linux und Mac laufen . Es kann sehr um Korrektheit kämpfen, bevor er ausgeführt wird, und der Coach kann mit zusätzlichen Metadaten kommentiert werden, zum Beispiel, um die Serialisierung zu leiten. Intermediate Language verwendet Speicherorte, um lokale Variablen zu speichern und verwendet einen Auswertungsstapel , um Vorgänge an Daten auszuführen. Intermediate Language hat Unterstützung für das Erstellen von Objekten, die Methoden beim Zugriff auf Felder aufrufen, integriert . Intermediate Language hat Unterstützung für das Erstellen und Manipulieren einer dimensionalen Erhöhung eingebaut . 10. Tipp 1: Vermeide Boxen und boxing: zuvor habe ich Ihnen gesagt, dass Boxen und Unboxing sich negativ auf die Leistung Ihres Codes auswirken, und Sie sollten es wann immer möglich vermeiden, besonders in geschäftskritischen Abschnitten. Aber wie schlimm ist die Leistung Overhead off Boxen und Unboxing? Ist es schlimmer die Sorge? Lassen Sie uns herausfinden, dass ich ein Programm geschrieben habe, um den Unterschied in der Leistung zwischen Methode zu messen , die nur Interjobs im Vergleich und Methoden verwendet, die genau dasselbe mit einer Objektvariablen tun. Hier ist der Code. Ich habe zwei Methoden. Messen Sie das Maß eines Mannes B. Die 1. 1 gemessene a nimmt eine ganze Zahl an und addiert den Wert eins dazu 1.000.000 mal. Ich verwende eine Stoppuhrklasse toe genau die Anzahl von Millisekunden, die benötigt wird, um die Schleife auszuführen . Die zweite Nachricht Measure B, tut genau das Gleiche, verwendet aber jetzt eine Objekttypvariable anstelle einer Ganzzahl. Alles andere ist gleich. Eine Million Wiederholungen auf Ich füge die Nummer eins während jeder Schleife hinzu. Es Oration. Wenn ich zu den Hauptmethoden scrolle, sehen Sie, dass ich damit beginne, beide Messmethoden aufzurufen und die Ergebnisse zu verwerfen. Ich mache das, weil es einen Beginn der Verzögerung in meinem Mantel geben könnte, der die Messergebnisse verzerren könnte. Um diese Verzögerung zu beseitigen, führe ich den kompletten Test aus. Sobald die Ergebnisse auf entdeckt wurden, führen Sie den Test erneut aus. Schließlich zeige ich die Ergebnisse in Millisekunden auf der Konsole für beide Methoden an. Ich gehe davon aus, dass die Methode A schneller sein wird. Also zeige ich auch an, wie oft Methode B langsamer ist als Methode A. Nein, ich habe dir bereits gesagt, Boxen und ein Boxen bringt eine Bedeutungsleistung ein. Overheads in deiner Kälte. Also lasst uns herausfinden, wie viel Overhead ich rede. Lassen Sie mich jetzt das Programm starten. Da hast du es. Nachricht A, die eine ganzzahlige Variable verwendet, dauert 10 Mikrosekunden. Methode B, die eine Objektvariable verwendet, dauert 55 Mikrosekunden. Methode B ist 5,5 mal langsamer als Methode A. Werfen wir einen Blick auf den Zwischencode. Ich beginne mit dem ganzzahligen Zusatz in Methode A. Wenn ich zwei Breakpoints sagte und weiter ausgeführt habe, dann wechseln Sie zur Disassemblierungsansicht. Los geht's. Sie sehen, dass der Zwischencode mit C-scharfen Quellcode kommentiert wird, so dass es wirklich einfach ist, die gewünschte Zeile zu finden. In diesem Fall. Die Addition in Methode A Hier ist es jetzt, bevor ich erkläre, was die Anweisungen tun, bedenken Sie, dass Zwischencodes einen speziellen Auswertungsstapel verwenden, um Berechnungen durchzuführen . Zahlen und Variablen werden mit Ladeanweisungen auf diesen Stack geladen, beginnend mit LD auf Ergebnissen werden wieder in Variablen mit Shop-Anweisungen gespeichert, die mit S t beginnen . auf Lager in Zwischenschicht . Also hier sind die vier Anweisungen. Der 1. Ladeort. Man lädt die erste lokale Variable, die zufällig die Variable A auf den Auswertungsstapel ist. Dann kommt die Lastkonstante eine Anweisung, die die Nummer eins bis zum Auswertungsstapel lädt. Die add Anweisung fügt die beiden oberen Zahlen auf dem Stapel zusammen, in diesem Fall der Wert aus einer unter Nummer eins. Und es ersetzt diese beiden Zahlen durch die Ergebnisse der Addition. Schließlich speichert der Laden Standort, eine Anweisung. Die Addition ergibt sich in der Variablen A. Nun schauen wir uns die gleiche Zeile in der Nachricht an. Verwenden Sie die Objektvariable. Hier ist es wieder. Wir beginnen mit der Lastposition eins, die die Objektvariable A bis zum Auswertungsstapel lädt. Denken Sie nun daran, dass eine Objektvariable ein Referenztyp ist, also haben wir jetzt einen Verweis auf ein Objekt auf dem Heap auf dem Auswertungsstock. Die nächste Anweisung muss also auf Unbox-Anweisung sein, da die Objekte auf der Hitze auf dem Wert im Inneren entpackt werden müssen, bis der Auswertungsstapel kopiert werden muss. Dann kommt die vertraute Lastkonstante eins, die die Nummer eins und dann die add Anweisung lädt, um die beiden Werte zusammen hinzuzufügen. Nein a ist eine Objektvariable, daher müssen die Ergebnisse der Addition in ein Objekt gepackt und auf dem Heap platziert . Die nächste Anweisung muss also Box sein, die genau das an Orten tut, ein Verweis auf diese neuen Objekte auf dem Heap auf der Auswertung stecken bleibt. Schließlich haben wir Speicherort eins, der die neue Objektreferenz unveränderlichen Tag speichert. So Methode eine Methode enthalten ziemlich die gleiche Schicht, eine niedrige Standortlast Konstante, dann eine Anzeige und schließlich einen Ladenstandort. Aber Methode B benötigt eine zusätzliche Unbox auf Box-Anweisung, weil wir auf begraben durchführen . Ihre Ergänzung zum Referenztyp der Lage im DOT-Net-Framework muss Daten zwischen diesem Stack und dem Heap verschieben , damit dies funktioniert. Der Unterschied in der Leistung ist also vollständig auf diese beiden Anweisungen Box auf Unbox zurückzuführen. Das Boxen beim Unboxen in Ihrem eigenen Code ist einfach. Vermeiden Sie einfach den Objekttyp. Aber wussten Sie, dass das Net-Framework voll von Klassen ist, die Objekte oder Objekte verwenden? Arrays für den internen Speicher, zum Beispiel, so ziemlich das gesamte System tut. Sammlungsname Space verwendet Objekt ein Rennen intern, so halten Sie es von den folgenden Klassen in Mission Critical Code entfernt. Eine weitere beliebte Klasse ist die Datentabelle in Systempunktdatentabellen, die den Wert aus jeder Zeile in einem Objektarray speichern . Sogar Datentabellen sind nicht sicher zu verwenden, da sie Typumwandlungen desselben Objektarrays verwendet haben. Um Ihren Code schnell zu halten, sollten Sie generische Sammlungsklassen im System verwenden. Gibt es bei Sammlungen keinen generischen Namensraum? Oder wenn Sie die Anzahl der Elemente im Voraus kennen, sollten Sie ein einfaches, eindimensionales Typen-Array für Datentabellen verwenden . Es gibt keine einfache Alternative. Wenn Sie damit davonkommen könnten, Ihre Codes neu berücksichtigen, um einen Datenleser zu verwenden, weil sie viel schneller als eine Datentabelle sind. Aber denken Sie daran, dass eine Datentabelle die Ergebnisse eines Datenbankvorgangs darstellt, der viele, viele Millisekunden in Anspruch nehmen kann . Es gibt keine großen Punkte, die den Datenabruf optimieren, der danach kommt. Es sei denn, Sie haben Millionen von Operationen am selben Tag ausgeführt, die Tabellenobjekte. Also, was haben wir gelernt? Casting Objekte, Variablen zu Werttypen? Einführung in die Unbox Anweisung in Zwischenmantel Speichern Werttypen in Objektvariablen führt eine Box Anweisung in Zwischencodes. Codes mit Büchern auf Büchern laufen bis zu fünf Mal langsamer als der gleiche Code ohne Anweisungen. Sie sollten vermeiden, dass ein Objekt in geschäftskritischem Code umgewandelt wird. Sie sollten vermeiden, nicht-generische Sammlungsklassen in geschäftskritischen Code auf. Und Sie sollten vermeiden, Datentabellen in geschäftskritischen Codes zu verwenden, aber nur, wenn Sie viele Vorgänge in derselben Datentabelle ausführen. Objekt 11. Tipp Nr. 2: Zeichenfolgen effizient addieren: in dieser Vorlesung möchte ich einen genaueren Blick auf die String-Verkettung oder das Hinzufügen von Strings werfen. Zusammen gibt es zwei Möglichkeiten, Zeichenfolgen zusammen hinzuzufügen. Die erste ist die, die wir alle richtig überall in unseren Codes verwenden. Wir haben gelernt, Variablen zusammen mit dem Plus-Operator zu String. Werfen wir einen Blick auf einen Code. Ich habe eine Anwendung gemacht, um die Leistung beim Hinzufügen von Strings zusammen zu messen. Ich beginne hier mit einer leeren Zeichenfolge und dann füge ich in dieser Schleife der Zeichenfolge 10.000 Mal ein einzelnes Zeichen hinzu. Nein, wenn ich das Programm ausführe, können Sie die Leistung hier im Ausgabefenster in Millisekunden sehen. Aber es gibt eine andere Möglichkeit, Strings zusammen hinzuzufügen. Der Teig Net Framework bietet eine spezielle Klasse namens String-Builder für gut bauen Strings. Wenn ich den gleichen Mantel mit einem String-Builder schreibe, beginnend mit einer leeren Zeichenfolge, indem ich ein einzelnes Zeichen 10.000 Mal hinzufüge, sieht der Code so aus, wie ich die Stiftmethoden anstelle des Plus-Operators verwende. Aber der Effekt ist derselbe beim Hinzufügen von Strings zusammen. Nein, lassen Sie mich ungewöhnlich die Messcodes für diese zweite Methode auf und führen Sie das Programm erneut damit wir die Ergebnisse vergleichen können. Chicken führt String-Verkettung mit dem Plus-Operator dauert 242 Millisekunden, aber die gleichen Codes mit einem String-Builder dauert nur eine Millisekunden. Der String Builder ist 242 mal schneller. Jetzt muss ich einen kleinen Haftungsausschluss machen. Wenn Sie dieses Experiment auf Ihrem eigenen Computer wiederholen, werden Sie schnellere Zeiten sehen, wahrscheinlich im Bereich von Hunderten und 50 bis 200 Millisekunden. Der Grund für meine langsamere Leistung ist, dass ich gerade ein Bildschirmaufzeichnungsprogramm im Hintergrund ausführe , auf dem CPU-Zyklen beim Zuweisen des verfügbaren Speichers aufgebraucht wird. Erinnerst du dich an den Vortrag über unveränderliche Strings? Ich habe gezeigt, dass die String-Klasse eine unveränderliche Klasse ist, was bedeutet, dass jede Änderung der Zeichenfolge eine neue Zeichenfolge erzeugt. Dadurch verhält sich die String-Klasse wie ein Werttyp, obwohl es sich tatsächlich um einen Referenztyp handelt. Was passiert im Speicher während des Luke, wenn ich der Zeichenfolge hinzufüge? Hier auf den Tafeln sind die ersten 3 Iterationen aus der Schleife. Sie können sehen, dass dies ein super ineffizienter Prozess ist. Jedes Mal, wenn ich Tante, ein Zeichen an der Schnur. Die gesamte Zeichenfolge wird in eine neue Zeichenfolge auf dem Heap kopiert. Mein Mantel macht zehntausende Speicherkopien hinter den Kulissen. Ein weiterer großer Nachteil dieses Mantels ist, dass er für 10.000 Iterationen eine Spur von de referenzierten String-Objekten auf dem Heap hinterlässt . Der Code lässt 9999 tote String-Objekte hinter sich. Der Müllsammler wird jetzt eine Menge Arbeit haben, die alles sauber macht. Kontrastieren Sie dies mit einem String-Builder. Ein String-Builder verwendet ein Zeichen-Array von einer bestimmten Standardlänge und schreibt einfach Text in das Array. Die ersten 3 Iterationen sahen also viel besser aus, meinst du nicht? Jede String-Addition schreibt ein Zeichen in das A. Dies ist viel effizienter, und wenn wir mit dem Stream fertig sind, haben wir einfach de referenziert Der String-Builder auf dem Garbage Collector muss nur einen einzelnes Objekt, so hier ist die Haupt nehmen weg von dieser Vorlesung. Wenn Sie Strings zusammen in Ihrem Code hinzufügen, verwenden Sie immer einen String-Builder. Versuchen Sie, Zeichenfolgen mit dem Plus-Operator zu vermeiden. Ich mache meine vorherigen Messungen nicht, indem ich 10.000 Ergänzungen durchführe. Aber wie stapelt sich dieser String-Builder, wenn ich nur ein oder zwei Ergänzungen mache? Lass uns herausfinden, was er meine Codes so findet. Ich habe jetzt zwei Schleifen. Der erste Blick macht nur eine begrenzte Anzahl. Streicherausgaben 2 bis 19. Die äußere Schleife wiederholt dieses Experiment 10.000 Mal. Andi summiert die Gesamtzeit. In Millisekunden gebe ich die Anzahl der Zugänge 2 bis 19 aus und für jeden Zusatz zählt. Ich zeige die Gesamtanzahl von Millisekunden für reguläre Strings für den String-Builder an. Lassen Sie mich das Programm laufen. Hier gehen wir dort für bis zu vier Ergänzungen. Reguläre Strings sind tatsächlich schneller als String-Builder über vier Ergänzungen. Der String-Builder ist schneller. Hier ist die Ausgabe des Programms, geplottet als Liniendiagramm. Die blaue Linie im Diagramm ist die Leistung von regulären String-Editionen. Sie können sehen, dass unregelmäßige Strings String-Builder bis zu vier Editionen übertreffen. Das sind fünf oder mehr Ergänzungen. Die String-Builder-Klasse wird effizienter, je mehr Ergänzungen Sie tun, Je größer die Leistung der String-Builder führt wird jetzt. Der Grund für dieses Verhalten ist, dass die Stream-Builder-Klasse einen gewissen Overhead beim Einrichten ihres internen Zeichen-Terrains hat , wobei die String-Flügel im Auge behalten werden. Und es ist interne Pufferkapazität beim Erweitern dieses Puffers bei Bedarf. Die Kegel, die reguläre Strings kurz vor einer Sequenz aus Speicher kopieren Operationen verwendet, um die Ergänzungen zu implementieren. Diese sind tatsächlich effizienter als der String-Builder, aber nur bis zu einem Punkt, und dieser Punkt ist genau vier Editionen. Wie sollten Sie String-Verkettung machen? Nun, wenn Sie vier Ergänzungen oder weniger haben, ist es nichts falsch daran, reguläre String-Variablen für den Plus-Operator zu verwenden. Dies wird Ihnen tatsächlich die beste Leistung geben, wenn Sie mehr als vier Strings zusammen hinzufügen möchten oder Sie nicht wissen, welche Anzahl von Hinzufügungen im Voraus verwendet wird, um den Builder zu string stattdessen, sobald Sie bis zu Tausende von Ergänzungen erhalten oder, in diesem Fall, 10 Tausend Editionen, genau dieser String-Builder ist tatsächlich mehr als 240 Mal schneller als normale String-Additions . Sie in diesem Szenario VerwendenSie in diesem Szenario immer den String-Builder 12. Tipp Nr. 3: Benutze den richtigen list: wenn wir eine Sammlung von Werten speichern möchten. Es gibt viele Möglichkeiten im Punktnetz-Framework. Wir haben die Sammlungsklassen in den Systempunktsammlungen. Name Leerzeichen. Wir haben generische Sammlungen im System. Hat Sammlungen. Hat generischen Namen Raum Onda. Wir haben regelmäßige Typen ein Rennen. Welcher Ansatz ist also der schnellste? Lass es uns herausfinden. Lass mich zu meinem Mantel wechseln. Ich werde mit der Messung der Performance oft Array-Liste beginnen. Ich habe hier eine Schleife, die der Array-Liste eine Million Ganzzahlen hinzufügt. Hier unten sind die Sekunden Missings, die auch eine Million Aufständische hinzufügen. Aber hier verwendet es generische Liste vom Typ Integer, um die Werte zu speichern. Lassen Sie uns die Codes ausführen, um die Leistung zu vergleichen. Hier gehen wir auf die Ergebnisse. Weit, die Array-Listencodes dauert 254 Millisekunden, während die generische Liste nur 42 Millisekunden dauert. Die generische Liste ist sechsmal schneller. Um den Grund dafür zu verstehen, wir einen Blick auf die Speicherlayouts für Ray List. Nach drei Ergänzungen sahen der Stapel und der Heap so aus. Nun, wenn Sie sich an den Vortrag über Boxen und Unboxing erinnern, erwähnte ich, dass viele Sammlungen in den Systempunktsammlungen Raum verwenden Objekte, Arrays für die interne Speicherung. Die Array-Liste ist keine Ausnahme, daher ist jedes Element in der Array-Liste ein Verweis auf eine Boxed Integer, die sich an einer anderen Stelle auf dem Heap befindet. Auf Wir haben gesehen, dass Boxen und Unboxing bringt eine Bedeutung Leistung. Overheads zu deinem Mantel. Vergleichen Sie dies nun mit dem Speicherlayout aus einer generischen Liste. Auch hier ist die Liste auf der Hitze, aber jetzt werden die Interjuroren auch direkt in den Listenelementen gespeichert. Und das liegt daran, dass eine generische Liste vom Typ Integer ein natives ganzzahliges Array für den internen Speicher in keinem Objektarray verwendet . Dadurch werden alle Objekte eliminiert. Referenzen aus den Listenelementen auf entfallen auch die Notwendigkeit für Boxen. Der Hauptzweck besteht also darin, immer generische Listen und Sammlungsklassen auf Vermeiden Sie die Klassen im Systempunktsammlungsnamensraum für geschäftskritische Codes zu verwenden . Jetzt versuchen wir einen Anzug Alternative. Ein natives ganzzahliges Array, das auf eine Million Elemente vorinitialisiert wurde. Ich schleife über jedes Array-Element und setze den Wert eins nach dem anderen. Lassen Sie mich den Mantel mit den anderen beiden vergleichen. Jetzt geht's los. Das ist viel schneller. Die Ausführung des nativen Arrays dauert nur 11 Millisekunden. Während die genetische Liste 52 Millisekunden benötigt, ist das native Array fast fünfmal schneller als die generische Liste. Der Grund für diese hervorragende Leistung ist, dass die Zwischensprache, die der C-scharfe Compiler kompiliert, auch native Unterstützung für ein Rennen hat. Werfen wir einen Blick auf meine Compiles kalt. Ich werde einen Haltepunkt einschalten, das Programm erneut einschalten dann zur Disassemblierungsansicht wechseln. Hier gehen wir weiter, und hier ist der Auftrag. Wie Sie sehen können, wird der Verweis auf das Array zuerst auf den Auswertungsstapel geladen. Dann wird das Array Offsets in das Element, in das wir schreiben möchten, auf den Stack geladen . Schließlich wird der Wert, der auf die Elemente gesetzt werden muss, dem Stack hinzugefügt. Und dann kommt eine Set Elements Instruction, die den Wert in die angegebenen Array-Elemente schreibt. So haben wir eine dedizierte Anweisung namens set-Elemente zum Schreiben von Werten in ein Rennen. Niemand des Codes, der ein Array verwendet, ist schnell. Jetzt. Vielleicht sinken Sie, dass ich betrüge. Da die Array-Liste und die generischen Listenklassen, die im internen Array verwendet werden, kontinuierlich Größe dieses Arrays ändern, wenn es überläuft, während mein Interviewer-Array bereits auf eine Million Elemente initialisiert wurde. Es sind also nie Überläufe. Lassen Sie mich also meine Codes ändern, um diesen Overhead zu entfernen. Ich werde zu der Zeile hier gehen, wo die Array-Liste initialisiert wird. Ich habe eine Standardkapazität von einer Million Elementen. Dann gehe ich runter zu dieser Zeile. Ich mache dasselbe für die generische Liste. Jetzt lassen Sie mich den Code erneut ausführen. Sie sehen, die generische Liste jetzt nur 31 Millisekunden benötigt, was 40% schneller ist als die vorherige Ausführung. Die Array-Liste dauert 187 Millisekunden, was eine Verbesserung gegenüber nur 26% darstellt. Das native Array dauert 12 Minuten. Sekunden auf ist immer noch die schnellste Option, 2,5 mal schneller als die generische Liste. Die eingebaute Unterstützung für Berets in der Zwischensprache sorgt dafür, dass sie Sammelklassen immer übertreffen werden . Die generische Liste und Array-Liste kann einfach nicht mit dieser Rolle Performance konkurrieren. Hier ist ein Diagramm Mit allen Leistungsergebnissen verbringt die Array-Liste die meiste Zeit damit, Ihre Daten vor der Dimensionierung der gebrechlichsten durch die richtige Anzahl von Elementen zu komprimieren, erhöht die Leistung um 40%, aber es ist immer noch nicht sehr gute Ergebnisse. Die generische Liste ist viel schneller, da sie die Einführung direkt in die Listenelemente auf dem Heap speichern kann , ohne eine zusätzliche Box-Referenz zu benötigen und die Liste auf die richtige Nummer zu skalieren . Off-Elemente steigern die Leistung um 26%. Das native Array bleibt die schnellste Option. Selbst bei präzisen Listen ist das Array immer noch 2,5-mal schneller als die genetische Liste. Wann sollten Sie also ein Rennen benutzen? Nun, wenn Sie geschäftskritische Codes auf der Anzahl von Elementen haben, ist im Voraus mit Array bekannt . Wenn Sie geschäftskritische Codes auf die Anzahl off Elemente nicht im Voraus wissen, verwenden Sie eine generische Liste. Vermeiden Sie die Klassen in Systempunktsammlungen. Sie verwenden Boxen auf unseren durch die generischen Klassen in System DOT-Sammlungen ersetzt nicht generische 13. Tipp Nr. 4: Benutze den richtigen Arraytyp: In der vorangegangenen Vorlesung haben wir generische und nicht-generische Kollektionen untersucht und mit nativen Raise verglichen. Es stellte sich heraus, dass native Arrays die schnellsten sind, aber generische Sammlungen haben den Vorteil, dass sie automatisch wachsen, dass Sie Portmore Elemente in sie. Also, wenn die Anzahl Off-Elemente im Voraus bekannt ist und Array die schnellste Option ist, aber das Punkt-Net-Framework unterstützt tatsächlich drei Arten eines Rennens. Der erste Typ ist das eindimensionale Array, das wir in der vorherigen Vorlesung gesehen haben. Alles, was mit den eckigen Ray Klammern in C Sharp deklariert wird, ist ein eindimensionales Array, zum Beispiel das hier gezeigte Interviewer-Array. Sie deklarieren das Array so, Andi, Sie greifen auf Elemente wie diese zu. Der zweite Typ ist ein mehrdimensionales Arrangement. Zum Beispiel ein zweidimensionales Array wie folgt deklariert, und Sie greifen auf Elemente wie diese zu. Der dritte Typ ist ein Jacket Array. Dies ist einfach ein Array aus einem Rennen. Sie deklarieren eine Jacke, eine Baskenmütze wie diese und erstellen dann eine neue Array-Instanz für jedes Top-Level-Elemente wie diese , dann greifen Sie auf und Elemente wie diese zu. Es wird ein Jacket-Array genannt, da jedes Element der obersten Ebene eine Differenznummer von untergeordneten Level-Elementen haben kann. Ein zwei-mal zwei-eckiges Array kann eine Buchse auf der rechten Seite wie diese haben. Wie vergleichen sich diese Arrays? Nun, gehen wir zu Xamarin Studio und finden heraus, dass ich ein Programm geschrieben habe, das initialisieren ist drei. Erhöhen Sie jedes mit einer Million Elementen. Hier ist das eindimensionale Array, mit 1000 mal 1000 Elementen in dieser Schleife Zeichen von Wert für jedes Element. Und hier ist das zweidimensionale Array, jetzt zu den Tausenden von 1000 Elementen initialisiert wurde. Ich habe zwei verschachtelte Schleifen, um jede Zeile und Spalte zu durchlaufen, und ich gebe jedem Array-Elementen Wert zu. Die dritte Nachricht verwendet jacken Array. Der Nachteil von Jacket-Arrays besteht darin, dass Sie das gesamte äußere Array mit Array-Instanzen initialisieren müssen. Der Code ist hier. Lassen Sie uns herausfinden, welches Array am schnellsten ist. Bereit. Los geht's. Das war wahrscheinlich keine Überraschung. Das eindimensionale Array ist mit 12 Millisekunden am schnellsten . Dann kommt das Jacket Array mit 16 Millisekunden und schließlich das zweidimensionale Array mit 24 Millisekunden. Jetzt werden Sie sich wahrscheinlich aus der vorherigen Vorlesung erinnern, dass die Zwischensprache native Unterstützung für eine dimensionale Löschung hat . Deshalb ist das eindimensionale Array am schnellsten auf dem gezackten Array kommt in Sekunde. Ein gezacktes Array ist einfach ein eindimensionales Array, das in einem anderen eindimensionalen Array verschachtelt ist. So erhalten Sie immer noch die Geschwindigkeitsvorteile aus der integrierten Unterstützung für Zwischensprachen. Vielleicht überraschend, das zweidimensionale Array ist die langsamste, und das liegt daran, dass ein zweidimensionales Array nachgegeben. Net ist nur eine Klasse ohne spezielle Laufzeitunterstützung. Hier, lassen Sie mich Ihnen meine Kompilierungen zeigen. Kalt. Wenn ich einen Haltepunkt setze, das Programm beim Schalter erneut zur Disassemblierung der Ansicht ons aus. Schauen Sie hier die richtige Zeile nach. Hier ist das eindimensionale Array. Sie können die vertrauten set-Elemente Anweisung sehen, die den Wert in das You are a -Element schreibt . Und hier ist die JAG Jittery zuerst eine Low-Elemente-Anweisung zu Lasten, die Referenz auf das innere Array und dann eine Set-Elemente-Anweisung, um einen Wert in dieses innere Array zu rechnet . Aber jetzt schauen Sie sich das zweidimensionale Array an. Es ist einfach ein Aufruf eines statischen Satzes. Methoden aus Ihrer A-Klasse eine Methodenaufruf für jeden Elementzugriff, plus was auch immer Implementierung in dieser Set-Methode ist, das sind viel mehr Codes, um sie auszuführen. Die eindimensionalen und gezackten Arrays. Jetzt haben Sie gerade gesehen, dass ein eindimensionales Array schneller ist als ein zweidimensionales Array, so dass wir ein zweidimensionales Array beschleunigen können, indem wir eine Technik namens Array-Abflachung verwenden. Werfen Sie einen Blick auf diese drei mal drei bereits aus ganzen Zahlen. Das sind insgesamt neun Elemente, die ich so auslegen kann. Ich habe jede Zeile von Elementen farblich codiert. Jetzt kann ich alle diese Daten auch in ein eindimensionales Array wie dieses packen, auf das ich zugreifen kann und Elemente, die die Zeile und Spalte so gegeben haben. Versuchen wir es also im Code. Ich habe hier ein Programm, das ein 1000 mal 1000 ganzzahliges Array einrichtet und dann alle Elemente durchsucht , die auf sie zugreifen. Hier ist der gleiche Mantel, aber mit einem eindimensionalen Array mit einer Million Elementen. Ich habe immer noch die beiden verschachtelten Schleifen, um auf All rose in allen Spalten zuzugreifen, aber jetzt habe ich die Übersetzungsformel verwendet, um die Zeile und Spalte in einen eindimensionalen Index zu glätten . Welche Mäntel glaubst du, dass es schneller ist? Es ist schwer zu sagen. Eigentlich wissen wir, dass das eindimensionale Array schneller sein wird. Aber jetzt haben wir auch die Gemeinkosten aus, die zusätzliche Multiplikation zu tun, um die Zeile und Spalte in einen abgeflachten Index zu übersetzen . Wird die zusätzliche Multiplikation die Gemeinkosten vom zweidimensionalen Array versetzt? Lass es uns rausfinden. Ich führe das Programm jetzt aus. Ich war hier, wir sind. Das eindimensionale Array ist immer noch die schnellste Option, mit 15 Millisekunden im Vergleich zum zweidimensionalen Array. Mit 22 Millisekunden ist das abgeflachte Array 1,5-mal schneller. Hier ist ein Diagramm mit allen Leistungsergebnissen. Das zweidimensionale Array ist zweimal langsamer als das eindimensionale Array. Selbst im Abflachungstest, wenn das eindimensionale Array die zusätzlichen Gemeinkosten ausgeschaltet hatte, um eine Multiplikation durchzuführen, ist es immer noch 1,5 mal langsamer. Dies deutet darauf hin, dass das Abflachen zweidimensionaler Arrays eine gute Idee ist. Vielleicht überraschend, hat das gezackte Array auch eine ziemlich gute Leistung . Es ist nur 1,3 mal langsamer als das eindimensionale Array im Vergleich zu den Abflachungsergebnissen, es gibt fast gleich 15 Millisekunden für die Abflachung eines dimensionalen Arrays auf 16 Millisekunden. Für das gezackte Array beträgt der Leistungsunterschied nur eine Millisekunden, das ist 6%. Also, wie sollten Sie ein Rennen verwenden, wenn Sie nur eine Dimension außerhalb Tag za haben, verwenden Sie eine dimensionale gelöscht für die beste Leistung. Wenn Sie zwei oder mehr Dimensionen von Daten haben, sollten Sie das Array abflachen. Wenn dies nicht möglich ist, sollten Sie ein Jacket-Array verwenden. Wenn es keine andere Option gibt, verwenden Sie ein mehrdimensionales Array. 14. Tipp Nr: 5: Vermeide Ausnahmen: in dieser Vorlesung. Ich möchte einen Blick auf die verantwortlichen Verwendungsausnahmen werfen. Also, was sind Ausnahmen? Das Net Framework verwendet Ausnahmen für die Behandlung von Fehlern. So funktioniert das also, wenn etwas in einem Codeblock schief geht, der Codes eine Ausnahme auslösen kann . Mit den throw-Anweisungen werden die Methoden sofort abgebrochen, auf dem Stack abgewickelt, was bedeutet, dass das Framework implizite Return-Anweisungen ausführt und von verschachtelten Methodenaufrufen abspringt, wenn der Aufruf-Stack höher und höher geht, bis es erreicht Mantel mit einem Versuch Fangblock. Das Framework springt in den ersten Catch-Block, der mit der Thronausnahme übereinstimmt, und beginnt mit der Ausführung des Codes There. Sie können die Ausnahme erfassen, indem Sie einen Parameter in den catch-Ausdruck einfügen, so dass sicher wie eine Menge Gemeinkosten klingt, nicht wahr? Wie groß glauben Sie, dass der Leistungsaufwand für das Werfen und Abfangen einer Ausnahme sein wird ? Nun, lass es uns herausfinden. Ich habe ein Programm geschrieben, das in diesem Fall sehr einfache Operation wiederholt eine Million Mal auf ganzzahlige Zahl erhöht. Hier sind die ersten Messmethoden, die einfach inkrementiert und ganzzahlige Variable ich war. Hier ist die zweite Messmethode, die gleichen Codes, aber nach der Inkrementierung der Variable wird der Code bei ungültiger Operation Beitritt ausgelöst. Jetzt befinden sich die throw-Anweisungen in einem try catch-Look, so dass das dot net Framework nicht benötigt wird, um den Stapel auf der Suche nach einem try catch-Block abzuwickeln . Wir befinden uns bereits in einem try catch-Buch, so dass die Ausführung direkt zur catch-Anweisung springt, die in diesem Fall absolut nichts tut. Dieser Code misst also die Gemeinkosten von einem einzigen Wurf und Fang. Es gibt keinen zusätzlichen Overhead, da der Stapel abgewickelt werden muss. Es gibt auch keine Gemeinkosten, da Ausnahmebehandlungscodes deaktiviert sind, da in diesem Fall der catch-Block anti-ready ist. Los geht's. Ich führe das Programm. Haben Sie erwartet, dass die Gemeinkosten, die eine Ausnahme auslösen, massiv inkrementieren, wobei eine Ganzzahlvariable 1.000.000 mal nur sechs Millisekunden dauert, aber das Auslösen von zwei Millionen Mal dauert zusätzliche 6,9 Sekunden, nicht nur Sekunden. Der Code, mit Ausnahmebehandlung, ist eine erstaunliche 1150 mal langsamer. Dies führt zum Hauptnehmen von diesem Abschnitt. Verwenden Sie niemals Ausnahmen in geschäftskritischen Codes. Fair genug. Ich werde einfach vermeiden, die Wurfaussagen in meinem Mantel zu verwenden, und wir sind in Ordnung? Nun, nein. Ich muss auch sicherstellen, dass die Bibliotheken in Basisklassen, die ich verwende, auch keine Ausnahmen auslösen . Die Realität ist, dass viele Klassenmethoden Ausnahmen verwendet haben, um dem Aufrufer eine nicht kritische Bedingung zu signalisieren . Wenn ich diese Ausnahmen vermeiden möchte, kann ich diese Methoden nicht direkt aufrufen. Aber ich muss mein Include ater gründlich auf Fehler überprüfen. Schauen wir uns den einfachen Anwendungsfall an. Ich werde das Programm ausführen, das Strings umwandelt, um einzuführen. Werfen Sie einen Blick auf diesen Code. Ich habe hier eine Nachricht namens Vorbereiten Liste. Es verwendet einen String-Builder, um eine fünfstellige Zahl mit einem Zufallszahlengenerator zusammenzustellen. Wenn die Zeichenfolge bereit ist, wird sie in dieser generischen Liste gespeichert. Der Code wird 1.000.000 Mal wiederholt, so dass ich irgendwann 1.000.000 Strings parsen muss. Die Messmethoden sind hier unten. Die 1. 1 durchläuft alle 1.000.000 Strings bei Anwendungen. Die Integer-Teile-Methode, um den Stream toe eine Zahl zu konvertieren. , Wenn beim Parsen etwas schief geht,löst die Teilemethoden eine frühere Ausnahme aus. Ich fange die Ausnahme hier auf, einfach den Fehler zu unterdrücken. Die Sekundenmessungen, Methoden tun das gleiche. Schleife durch alle Saiten auf Parsons eins nach dem anderen. Aber jetzt benutze ich den Versuch. Parse-Methoden anstelle des Parson ist nicht versuchen. Teile werden versuchen, den Stream zu analysieren, einfach nichts zu tun. Wenn das Parsen fehlschlägt, wird es nie eine Ausnahme auslösen, daher muss ich das Catch-Buch nicht ausprobieren. Hier. Sie können den Unterschied in Sündengesprächen sehen. Try Pars gibt einen booleschen Wert zurück, der angibt, ob der Parson erfolgreich war, es hat auch einen Ausgabeparameter, um das Ergebnis zu speichern. Vergleichen Sie das mit den Parse-Methoden, die einfach die übergebenen Ergebnisse direkt zurückgeben und keine Parameter oder Argumente haben, um anzuzeigen, ob das Parsen erfolgreich war oder nicht. Okay, zurück zu den Methoden der Vorbereitungsliste. Werfen Sie einen Blick auf die Küste. Siehst du am Ende etwas Seltsames aus meinem Charakter-Array? Ich habe einen zusätzlichen Buchstaben X. Der Zufallszahlengenerator erzeugt hier eine Zahl zwischen Null und 10, also 10% Rabatt auf die Zeit dass der Buchstabe X dem Stream anstelle einer gültigen Ziffer hinzugefügt wird. Wenn ich einen Breakpoint direkt über die Methode hinaus setze, rufen Sie das Programm ausführen auf und überprüfen Sie dann den Inhalt von der generischen Liste. Dann sehen Sie, dass ein paar Zahlen ungültig sind, weil sie auf X anstelle einer Ziffer enthalten . Die Parse- und Try-Teile-Methoden schlagen bei diesen Zahlen nur für die Teile fehl. Methoden werden eine Ausnahme auslösen, wenn dies geschieht. Basierend auf dem, was wir bereits über Ausnahmen wissen, da sie wirklich langsam sind, erwarten wir, dass die try-Parse-Methoden viel besser als die Teile-Methode ausführen. Wie groß wird der Unterschied wohl sein? Lass es uns herausfinden. Ich werde das Programm ausführen, ein Messen Sie den Unterschied in der Leistung. Los geht's. Und hier sind die Ergebnisse. Die Tri Parse Methoden dauert 618 Millisekunden nach Rom. Die Teilemethoden dauern 3727 Millisekunden, daher ist die Parse wegen der zusätzlichen Ausnahmebehandlung sechs Mal langsamer als try pars. Wir haben also gesehen, dass Datenzeitkonvertierungen auch problematisch sein können. Missionskritischer Mantel. Aufgrund der Tatsache, dass viele Konvertierungsmethoden Ausnahmen auslösen, wenn die Eingabedaten ungültig sind, gibt es andere häufige Situationen, in denen Ausnahmen ausgelöst werden? Ja , ich werde dir noch eins zeigen. Also hier ist ein anderes Programm, und in diesem Programm mache ich das Gegenteil. Aus dem größten Programm. Ich wandle ganze Zahlen zurück zwei Strings. Ich habe hier eine vorbereitete Liste Nachricht. Wachen. Jetzt füllt es einen generischen Individualisten mit einer Million zufälligen Ganzzahlen. Andi Down hier ist die ersten Messmethoden, die jede ganze Zahl in der Liste durchlaufen, und für jede Zahl verwendet es eine Nachschlagetabelle, um diese Zahl in eine Zeichenfolge zu konvertieren. Wenn Sie hier nachschlagen, werden Sie sehen, dass ich in Ihrem Zeichenfolgenwörterbuch vordefiniert habe, um nach einer Tabelle zu suchen , indem Sie die Nachschlagetabelle mit einer Ganzzahl indizieren. Ich kann schnell die Zeichenfolge finden, die diesem Interview entspricht. Also zurück zu den Messcodes. Ich verwende die Nachschlagetabelle, um die Zeichenfolge zu finden und sie in der Variablen s zu speichern Wenn etwas schief geht , werden diese catch-Anweisungen die Ausnahme beim Unterdrücken des Pfeils abfangen. Werfen wir nun einen Blick auf die zweiten Messmethoden. Es macht genau das Gleiche, aber es hat einen zusätzlichen Check hier. Ich teste, ob die Ganzzahl tatsächlich im Wörterbuch ist, bevor ich sie verwende, um den entsprechenden Stream zu suchen . Dies, außer Überprüfung, vermeidet eine Ausnahme. Und deshalb brauche ich keinen Versuch. Kassenblock. Okay, zurück zur Nachricht der Vorbereitungsliste. Siehst du den in Validator? Es ist sehr einfach. Ich habe 10 Einträge im Wörterbuch für die Ziffern von 0 bis 9, aber der Zufallszahlengenerator generiert Zahlen zwischen Null auf 10. Also in 10% Rabatt auf die Fälle habe ich die Nummer 10 in der Liste in der Nachschlagetabelle hat keinen Eintrag für diese Nummer. Wenn ich den Breakpoint direkt über das medizinische hinaus lege, das Programm ausführen und dann den Inhalt von der generischen Liste inspizieren, dann können Sie sehen, dass ich gelegentlich eine 10 in der Liste habe. Das Wörterbuch nachschlagen, wird für diese Nummer und nur für die Kennzahl fehlschlagen. Eine Methode wird eine Ausnahme auslösen, wenn dies geschieht. Basierend auf dem, was wir über Ausnahmen bei den Performance-Overhead-Off-Ausnahmen wissen, erwarten wir, dass Maß A langsamer ist als Measure B , also lassen Sie uns überprüfen, ob ich das Programm ausführen und den Unterschied in der Leistung messen werde . Hier gehen wir und hier sind die Ergebnisse messen eine dauert 1180 Millisekunden zu laufen Maßnahme dauert nur 167 Millisekunden, so Messen A ist sieben Mal langsamer als Maßnahme wegen der zusätzlichen Ausnahmebehandlung . Hier sind alle Messergebnisse Kombiniert in einer einzigen Referenz unten sind hier Teile und versuchen Sie Pars mit der Teile-Methode, die 3727 Millisekunden auf Try Pars nimmt, die nur 618 Millisekunden benötigen, weil es keine Ausnahme auslösen muss, wenn die Importdaten ungültig ist, ist dies ein Rückgang der Laufzeit von über 80%. Dann dauert der Sekunden-Test mit den Wörterbuch-Looks, die einen Nachschlagevorgang zur Behandlung von Ausnahmen durchführen , 1180 Millisekunden. Aber wenn wir zuerst den Schlüssel überprüfen und dann das Nachschlagen durchführen, vermeiden wir Ausnahmen alle zusammen. Auf der Flucht sinkt die Zeit wieder auf nur 167 Millisekunden, ein Rückgang der Laufzeit um mehr als 80%. Was sind also die Best Practices für die Verwendung von Ausnahmen? Nun, wenn Sie Ausnahmen in einer geschäftskritischen Schleife verwenden, dann verwendet sie nur, um einen tödlichen Zustand anzuzeigen, der erfordert, dass Sie an Bord der Schleife vollständig. Verwenden Sie keine Ausnahmen für nicht schwerwiegende Bedingungen, die Sie behandeln, indem Sie einfach zur nächsten Schleifeniteration wechseln . Nicht setzen. Versuchen Sie, Blöcke in tief verschachteltem Code oder in Low-Level-AP-I-Funktionen zu fangen, weil sie Ihre Mäntel verlangsamen. Setzen Sie die Ausnahmebehandlung so nah wie möglich an das Hauptprogramm. Verwenden Sie niemals einen generischen Fang. Ausnahmeanweisungen, die alle Ausnahmen zwischenspeichern, da sie auch nicht-fatale Bedingungen abfangen. Es wird nicht sofort offensichtlich sein, warum Ihr Code langsam ist. Wenn Sie ein A schreiben, verwende ich keine Ausnahmen für nicht kritische Randbedingungen wie das Konvertieren ungültiger Daten oder Fehlschlagen eines Nachschlagevorgangs. Betrachten Sie das try-Parse-Muster dafür mit einem booleschen Rückgabewert, der Erfolg anzeigt einen Out-Parameter für die Rückgabe von Daten. Was auch immer Sie tun, verwenden Sie niemals Ausnahmen, um den Fluss Ihres Programms zu steuern. 15. Tipp Nr. 6: Verwenden für anstelle für foreach: In dieser Vorlesung geht es um die Optimierung von Schleifen. Ein häufiger Rat, den Sie oft hören, um Schleifen zu beschleunigen, ist, dass Sie vier Anweisungen anstelle von jeder verwenden sollten , um über die Sammlung zu schauen. Aber ist das wahr auf, Und wenn ja, wie groß ist der Unterschied in der Leistung zwischen den beiden? Beginnen wir mit der Mechanik von Z vier und für jede Aussage beginne ich mit vier. Wenn Sie eine for-Schleife verwenden, um auf Elemente in einer Sammlung zuzugreifen, beginnen wir normalerweise mit einer Schleife, die von Null bis zu der Zahl off Elemente in der Sammlung Minds eins zählt . Dann verwenden wir auf Index des Ausdrucks, um auf jedes Element zuzugreifen. Im Gegenzug ist ein vier Luke nur möglich, wenn Sie direkten Zugriff auf Elemente in der Sammlung haben, meist über die eckigen Klammern. In zusätzlicher Meditation in C scharf listet das Array generische Liste auf Array-Klassen auf. Alle unterstützen die Indizierung. Eine andere Möglichkeit, auf Elemente in einer Sammlung zuzugreifen, ist die Verwendung im Zähler hinter den Kulissen. Die für jede Anweisung beginnt mit der Erstellung eines neuen in Zähler Objekt in anderen Rasierer haben nur drei Mitglieder. Ein aktuelles Feld, das den aktuellen Auflistungselementwert enthält, um zum nächsten Element zu wechseln. Andi setzt Methoden zurück, um zum Anfang der Sammlung zu springen. Daher richtet jede Anweisung eine Schleife ein, die move next aufruft, bis sie das Ende der Sammlung erreicht . Während jeder Schleifeniteration enthält das aktuelle Feld den aktuellen Elementwert. Also, welche Aussage ist schneller? Es ist schwer zu sagen. Eigentlich erfordert die vier Staatsmann einen Index, so dass bei den Kosten aus, direkt auf alle Elemente zugreifen können hoch sein kann. Aber auf den anderen Händen, Ford muss jeder auf inem Belüfter Objekt einrichten auf, dann rufen Sie die Bewegung nächste Nachricht in jeder Schleife, es oration. Es hängt also alles davon ab, was schneller ist, der Indexer oder die Bewegung. Nächste Methoden Hier sind die Vor- und Nachteile von jeder Methode. Die vier-Anweisung ist potenziell die schnellste wegen ihrer Einfachheit, aber sie erfordert eine Sammlung mit einem Indexer. Auf Sammlungen nicht im Voraus wissen, in welcher Reihenfolge die Elemente durch den Indexer zugegriffen werden, so dass alle Werte müssen zuerst im Speicher geladen werden, denn jeder ist komplexer, weil er einen enormen Krater hinter den Kulissen verwendet, und es erfordert einen Aufruf der move next Methoden, um zum nächsten Element zu gelangen. Aber der Vorteil im Betrieb ist, dass es auf jeder Sammlung funktioniert. Ein weiterer Vorteil ist, dass der aktuelle Wert auf Nachfrage berechnet wird, so dass die gesamte Sammlung nicht im Speicher sein muss, um eingeweiht zu werden. Beratung im Internet etwa vier und für jeden ist gemischt, manche sagen immer für verwendet. Aber andere sagen, die Leistungsverbesserungen sind es nicht wert. Lassen Sie mich Ihnen also ein Programm zeigen, das die Leistung von beiden Aussagen für eine Vielzahl von Sammlungen misst . Ich habe das Programm geschrieben, das drei Sammlungen von 10 Millionen Ganzzahlen einer Array-Liste aufbaut , ein generisches Interview, das auf einem regulären Integer-Array veröffentlicht wurde. Die Listen werden in dieser Methode initialisiert. Hier. Vorbereiten von Listen. Die Nachricht generiert 10 Millionen Zufallszahlen zwischen Null und 255 auf Anzeigen. Sie Zehe alle drei Listen, dann kommen zu Messmethoden. Hier unten sind sechs von ihnen. Ich fange mit der 1. Maßnahme an. Ein Eins ist durch alle 10 Millionen Elemente aus der Array-Liste mit einer einfachen for-Schleife lose und verwendet den eingebauten Indexer aus der Iraner-Klasse, um auf die Elemente zuzugreifen. Jetzt wird die nächste Message A gemessen, um auch alle Elemente aus der Array-Liste durchlaufen zu lassen. Aber jetzt, mit einer für jede Anweisung anstelle der vier Anweisungen, so dass hinter den Kulissen für jede in Zählerobjekte erstellt werden, die sequenziell durch jedes Element in der Sammlung Schritt . Die nächsten beiden Methoden sind Maßnahme Be eine Tante Maßnahme B zwei. Sie machen genau das Gleiche, aber mit dem generischen begraben Ihre Liste anstelle der Array-Liste. Und schließlich habe ich die Methoden Measure C eins und Measure C zwei. Wieder haben sie alle 10 Millionen ganze Zahlen durchsucht, aber jetzt verwenden sie das Integer-Array. So sehen Sie, dass alle drei Sammlungsklassen sowohl die Indizierung in Bewunderung unterstützt, was am schnellsten sein wird. Lassen Sie uns herausfinden, dass ich das Programm jetzt ausführe, also warten wir auf die Ergebnisse. Und hier sind die Ergebnisse. Verwendung von vier gefallenen Array dauert 98. Millisekunden für jedes Alan Array verwendet Hunderte und drei Millisekunden, ein winziger Unterschied. Aber die Verwendung von vier und für jede auf der genetischen Liste dauert jeweils 300 Achter auf 501 Millisekunden, und wir erhalten den größten Unterschied in der Leistung, wenn vier und für jede in einer Array-Listeverwendet Array-Liste werden. 302 auf 872 Millisekunden. Sehen Sie das für eine Gehaltserhöhung? Der Unterschied ist sehr klein bei der Optimierung von A für jede zwei oder vier ist wahrscheinlich die Mühe nicht wert . Aber für die generische Liste und die Array-Liste ist der Unterschied ziemlich groß. Warum ist das so? Werfen wir einen Blick auf die Zwischenkälte. Ich beginne mit der Measure C eine Methode, die eine einfache for-Schleife verwendet, um auf jedes Element in einem Integer-Array zuzugreifen . Also hier ist die for-Schleife. Diese drei Zwischensprachenanweisungen setzen die Variable um 20 Dann springen wir zur Position eins F auf, um die Variable I mit dem Wert von 10 Millionen zu vergleichen. Wenn ich weniger als 10 Millionen bin, fahren wir hier auf Execute the loop body fort. Sie können die vertraute Lastelementanweisung hier sehen, um auf Array-Elemente zuzugreifen. Schließlich die Codes hier weiter, wo wir nicht wieder eins zu der Variablen durch Check sind. Wenn es jetzt weniger als 10 Millionen ist, schauen wir uns die gleichen Schichten in der Measure C two Methode an. Es ist fast das gleiche Herunterfahren der Indexvariablen auf Null und springt zu Position 24, wobei der Index mit der Länge außerhalb des Arrays verglichen wird. Andi weiter, wenn es weniger ist. Aber sieh dir dieses Stück hier an, kurz bevor du den neuen Körper ausführst. Wir haben vier Anweisungen. Laden Sie den Speicherort für den Ladeort. Drei Ladeelemente am Lagerort. Eins. Diese vier Anweisungen. Rufen Sie die aktuellen Array-Elemente ab und speichern Sie sie an Position eins, die der Variablen I entspricht. Daher ist der Unterschied zwischen vier und A für jede Anweisung für reguläre Arrays nur diese vier Zwischensprache Anweisungen dazu ist, warum der Leistungsunterschied zwischen den beiden so klein ist. Der Compiler erstellt keine tatsächlich in Zählerobjekten mit Strömen und bewegt sich als nächstes. Stattdessen es ein leicht modifiziertes Vier-Look aus, um die Enumeration zu implementieren. Sehen wir uns nun die genetischen Listen an. In der Maßnahme. Seien Sie eins auf Maßnahme B zwei Methoden. Die Maßnahme, sei eine Methode, ist sehr unkompliziert. Zuerst erhalten wir hier den vertrauten Satz von Anweisungen für die vier Schleife. Sie dann WennSie dannauf die Listenelemente zugreifen, verwenden die Codes einen virtuellen Aufruf eines Indexers. Anrufe erhalten Artikel. Der Rückgabewert ist eine Ganzzahl, die in Position gespeichert wird. Aber Maßnahme B zwei ist sehr unterschiedlich. Die für jede Anweisung beginnt mit dem Aufruf der get in numerator-Methoden für die Listenobjekte . Die Liste im Zähler ist eigentlich eine Struktur und keine Klasse auf. Es wird in Position zu den Codes gespeichert, dann ruft. Bewegen Sie den Operator in als Nächstes und fährt fort, wenn das Ergebnis wahr ist. Als nächstes erhält er den aktuellen Elementwert, indem er die Eigenschaft aufruft, Ströme abruft und die Ergebnisse an Position 1 speichert. Dann wird der Schleifenkörper ausgeführt, und dann sind wir wieder in Bewegung als Nächstes. Dieser Mantel ist viel komplexer als einfach auf Listenelemente über den Index zuzugreifen. Verbessert, läuft es langsamer. Schauen wir uns nun die Array-Liste in Methoden an. Messen Sie eine £1 Maßnahme A. Zu gibt es nichts Besonderes in Measure A, eine reguläre for-Schleife bei einem virtuellen Aufruf des gets-Element-Indexers. Aber sieh dir das hier an. Die get-Item-Injektion gibt und Objekte zurück, keine Ganzzahl. Bevor wir also die Ergebnisse speichern können, muss es mit dieser unbox-Anweisung hier entpackt werden, und wir haben bereits gelernt, dass Boxen und Unboxen eine wichtige Performance Overhead in Ihrer Küste bringt . Jetzt ist hier gemessen. A zu diesem Fell sieht sehr ähnlich wie die genetische Liste Kult in Maßnahme B zwei. Aber schauen Sie sich die Unterschiede hier zuerst an. Die Array-Liste im Zähler ist eine Klasse auf nicht umrippt, was bedeutet, dass die cool, um Strom zu erhalten, ein virtueller Aufruf anstelle eines regulären Aufrufs ist. Dies mag wie ein triviales Detail erscheinen, aber die colvert-Anweisung ist tatsächlich langsamer als die Aufrufanweisung. Aber zweitens, erhalten Sie aktuelle Rückgaben mit Objekten und nicht einer Ganzzahl. Das Ergebnis muss also wieder entpackt werden, bevor es gespeichert werden kann, was hier mit der Unbox-Anweisung passiert. Also hier ist eine Bedeutung. Nehmen Sie keine Generika weg. Operatoren geben immer den aktuellen Wert als Objekt zurück. Wenn Sie sie also in Betriebsüberwert-Typen verwenden, werden sie im Hintergrund immer verwendete generische Enumeratoren entpacken, wenn möglich. Hier sind alle Messergebnisse kombiniert in einem Diagramm mit £4 für jede auf einem Array dauert, beziehungsweise 98 unter Hunderten und drei Millisekunden mit vier und für jede auf einer genetischen Liste dauert jeweils 308 und 501 Millisekunden bei der Verwendung von vier und für jede in einer Array-Liste dauert jeweils 302 auf 872 Millisekunden. Wir haben gesehen, dass es sehr wenig Unterschied zwischen vier unter für jeden gibt, wenn ein reguläres Array betrieben wird. Der Grund dafür ist, dass der Compiler Array und Bewunderung optimiert, indem er eine leicht modifizierte Vier-Schleife ausgibt . Anstatt den Ärger zu durchlaufen und auf den in-Operatorobjekten mit generischen Listen zu erstellen, sehen wir einen klaren Unterschied zwischen vier. Und für jeden ist der Zähler anweisen, was ein Werttyp ist, und so kann er Werttypen wie Ganzzahlen effizient speichern und dennoch in Bewunderung, ist immer noch 1,6 mal langsamer als die Verwendung einer for-Schleife. Hier macht es also Sinn, eine für jede Schleife zu optimieren, indem sie sie in eine for-Schleife umschreibt . Und schließlich, wenn wir uns eine Ray-Listen ansehen, haben wir gesehen, dass hinter den Kulissen eine Menge Unboxing los ist. Der Indexer gibt zurück. Ein Objekt, das in eine ganze Zahl auf dem in-Operator umgewandelt werden muss, ist eine Klasse und nicht destruct on. Es gibt den aktuellen Wert als ein Objekt zurück, das wieder in eine ganze Zahl umgewandelt werden muss in Betrieb auf einer Array-Liste hat eine große Strafe . Es ist zwei Punkte achtmal langsamer als die Verwendung einer for-Schleife. So wählen Sie zwischen vier und für jeden. Wenn Sie ein Array verwenden, müssen Sie nicht ein für jeden in eine Vier umschreiben. Der Unterschied in der Leistung ist es einfach nicht wert. Wenn Sie eine generische Liste verwenden, ist die Verwendung einer vier Anweisungen 1,6-mal schneller als die Verwendung von a für jede Anweisung, so dass das Umschreiben für jede Anweisung in eine for-Schleife definitiv wert ist. Und für Array-Listen sind die Verbesserungen noch größer. Eine Vier-Anweisung auf einer Array-Liste ist 2,8 mal schneller. Aber wenn Sie über all das Boxen und Unboxing im Hintergrund nachdenken, möchten Sie vielleicht die Array-Liste in eine generische Liste umschreiben . Und schließlich, wenn Sie für jede verwenden und Sie sind in Betrieb eine Sammlung off Wert Zeiten, immer stellen Sie sicher, dass Sie die generische Anoma Reiter, die in der E eine nominale Kräutertee-Schnittstelle auf nicht die nicht-generische in Zähler, der in der E unzählige Schnittstelle auf ist. Der Grund dafür ist, dass der nicht-generische Operator immer den aktuellen Wert als Objekt zurückgibt , so dass Ihr kompilierter Mantel viele Offbox- und Unbox-Anweisungen enthält und ein generisches in Erinnerungen verwendet , das dies vermeidet. 16. Wie funktioniert der garbage: Dieser Vortrag ist durch Anfragen aus saugen. Es ist lustig, wer mich gebeten hat, in die Müllsammlung zu schauen? Danke für die Bitte, Saugnäpfe. Ich hoffe, Ihnen gefällt dieser Vortrag und ich hoffe auch, dass ich Ihren Namen richtig ausspreche. Wenn jemand anderes spezielle Wünsche zu dem Thema hat, an das ich mich wenden soll, senden Sie mir eine Nachricht und ich werde sie in meine Roadmap oder zukünftige Lektionen einarbeiten. Wenn Sie sich in der zweiten Vorlesung dieses Kurses erinnern, der im Heap-Speicher im Fundamentals-Abschnitt, haben wir gesehen, was passiert, wenn Referenztypvariablen außerhalb des Geltungsbereichs gehen. Die Variablen, die auf dem Stapel vorhanden sind, werden auf den entsprechenden Objekten auf dem Heap zerstört . R D referenzierte dereferenzierte Objekte bestehen weiterhin und werden nicht sofort zerstört. Ich habe kurz erwähnt, dass ein separater Prozess, der als Garbage Collector bezeichnet wird, diese Objekte regelmäßig aufräumt. In dieser Vorlesung werden wir uns den Müllsammler genauer ansehen. Was ist der Garbage Collector und wie funktioniert er? Beginnen wir mit einem sehr einfachen Programm. Dieses Programm hat nur eine Meldung mit einem lokalen Variablenobjekt-Array. Das Array wird mit fünf Objekten auf diesen fünf Objekten initialisiert, befinden sich ebenfalls auf dem Heap neben dem Array selbst. Nun, um die Dinge interessanter zu machen, lassen Sie uns Array-Elemente zwei und drei entfernen, indem Sie sitzen. Es Array-Elemente, um die entsprechenden Objekte zu kennen. Nummer zwei und drei gibt es immer noch auf der Hitze, aber jetzt sind sie de referenziert. Es gibt keine Verweise auf diese Objekte von irgendwo im Mantel. Was passiert, wenn der Müllsammler in die Tür tritt? Netz. Garbage Collector ist ein Markierungs- und Sweep-Sammler, der verlässt. Es gibt zwei verschiedene Stufen von Müll sammeln eine Markierungsbühne auf einer Sweep-Bühne während der Markierungsphase. Der Garbage Collector markiert alle Lebensobjekte auf dem so in diesem Beispiel, das wäre das Array selbst und die Objekte 01 auf vier. Die Objekte zwei und drei werden übersprungen, da es keine Verweise auf diese Objekte von irgendwo im Mantel gibt. Als nächstes kommt die Sweep-Stufe alle Objekte, die nicht in der vorherigen Stufe R D Referenz auf markiert haben , und in dieser Phase werden sie de aus dem Heap zugeordnet. In diesem Beispiel wurden die Objekte zwei und drei nicht markiert, und daher sind sie die zugewiesenen auf. Das hinterlässt eine Art Loch auf dem Haufen. Der Hundenetz-Müllsammler führt nach dem Sweep einen zusätzlichen Schritt durch, der als kompakt bezeichnet wird. In der kompakten Phase werden alle Löcher auf dem Heap entfernt, so dass in diesem Beispiel das Objekt vier nach oben verschoben wird, um das Loch zu füllen Das Objekt hinter der Markierung und Sweep Garbage Collector ist sehr gut zu lokalisieren. Jedes einzelne Deal referenzierte Objekt auf dem Heap beim Entfernen. Aber es hat auch einen großen Nachteil. Es ist sehr langsam. Während der Markierungsphase muss der Garbage Collector jedes Objekt im Speicher überprüfen, um festzustellen, ob es sich um ein Leben oder um das referenzierte handelt . Wenn sich viele Tausende von Objekten auf dem Heap befinden, wird Ihr Programm tatsächlich für eine Weile einfrieren, da der Garbage Collector jedes einzelne Objekt inspiziert . Der Prozess ist auch sehr ineffizient, da lange lebende Objekte auf dem Heap überprüft und neu überprüft werden , jeden Zyklus durchhält, da sie jederzeit d referenziert werden könnten. So könnte eine sehr lange lebende Objekte Hunderte von Malen überprüft werden, wenn es noch am Leben ist. Die Lösung für dieses Problem wird Generationen-Garbage Collector genannt, der Dot Nets-Garbage Collector ist generationell und hat drei Generationen, die Sie als drei separate Heaps visualisieren können. Alle neuen Zuweisungen gehen in den ersten Generationen-Heap namens Generation Zero. Also, wenn wir das Testprogramm mit dem Fünf-Element-Array mit den Elementen zwei und drei erneut besuchen, setzen Sie keine. Dann würden die Speicherlayouts so aussehen. Alles ist das gleiche wie zuvor, aber jetzt befinden sich alle Objekte in Generation Null-Generationen. Einer kommt zu unserem Anti. Der erste Sammlungszyklus führt eine Markierung und Sweep für alle Objekte aus, die die Sweep-Bewegung zu Generation 1 überlebt haben . Nach einem Zyklus sieht das Speicherlayout wie dieses Z-Array aus. Andi Objekte 01 und vier haben den Sweep überlebt und sind jetzt in Generation eins. Stellen Sie sich nun vor, dass das Programm an diesem Punkt weitergeht. Es setzt ein neues Objekt fünf in Array-Elementen zu allen neuen Zuordnungen. Gehen Sie in eine Generation Null, damit die Speicherlayouts so aussehen würden. Wie Sie sehen, ist dies eine interessante Situation. Das Array rezitiert in Generation eins, aber seine Elemente sind in Generationen Null und eins. Das ist vollkommen gültig. Jetzt. Der Garbage Collector tritt erneut für einen zweiten Zyklus ein. Alle Generation 1 Objekte bewegen sich auf Generation auf das neue Objekt in Generation Null bewegt sich zu Generation eins, wenn das Programm fortfährt und setzt ein neues Objekt sechs in eine seltene Elemente drei, es würde wieder in Generation Null gehen. Wir haben jetzt ein Array in Generation, um auf Objekte der Generation 01 Bezug zu nehmen, um wieder Beweise für die Gewalt. Vielleicht fragen Sie sich an diesem Punkt, warum das alles passiert? Warum haben diese drei Generationen? Nun, die Antwort ist sehr einfach. Generationen helfen, die Anzahl von Objekten in Generation Null zu begrenzen. Jeder Sammlungszyklus löscht die Generierung Null von allen Objekten vollständig. Im nächsten Zyklus muss der Garbage Collector also nur neue Objekte untersuchen, die nach dem letzten Zyklus erstellt wurden . Natürlich verschwindet das Problem nicht. Der Garbage Collector hat die Objekte einfach irgendwo anders verschoben. Aber hier sind die Schlüsselgenerationen, die sehr selten gesammelt werden. Der Müllsammler geht davon aus, dass alles, was die Generation erreicht, ein lang lebendiges Objekt sein muss , das nicht sehr oft überprüft werden muss, also löst dies zwei Probleme. Erstens reduziert es die Anzahl von Objekten in Generation Null, so dass der Garbage Collector weniger Arbeit hat, um zweite, lang lebende Objekte zu tun , die in die Generation überleben, um mich nicht sehr oft überprüft, was genau was wir wollen. Der Generationen-Garbage Collector ist ein schöner Hochleistungsalgorithmus, aber er hat einen wichtigen Nachteil. Es ist ineffizient, da große, lange lebende Objekte verarbeitet werden. Betrachten Sie eine große und lange verlassene Objekte werden in Generation Null zugewiesen. Es überlebt den ersten Zyklus, und die Wärme wird verdichtet, was das Objekt möglicherweise im Speicher bewegt. Dann bewegt es sich zur ersten Generation. Es wird auf Bewegungen zur Generation an alle verdichtet. Insgesamt sind dies zwei Verdichtungs- und zwei Verschiebungen, also ignoriert insgesamt vier Speicherkopieoperationen für ein einzelnes Objekt, bevor es in der Generation auf dem Garbage Collector ankommt es für eine Weile. Wenn das Objekt sehr groß ist, können diese vier Kopiervorgänge pro Objekt zu einer Signifikanz-Performance-Gemeinkosten führen. Die Lösung für dieses Problem besteht also darin, zwei separate Heaps zu haben, einen für kleine Objekte auf einem. Bei großen Objekten sieht das Design ungefähr so aus. Indoor-Netze, die zu Hüften sind. Das kleine Objekt. Er, der mit den drei Generationen arbeitet, die wir vorher besprochen haben, und der große Kulturtipp, das Besondere an dem großen Objekthaufen ist, dass er keine Generationen verwendet. In der Tat hat es nur eine einzige Generation, die mit der Generation synchronisiert wird, um das kleine Ziel zu entfernen. Also, wenn die Garbage Collect Prozesse Generierung bis zum kleinen von Ihrem Team waren, läuft es auch durch das gesamte große Ziel. Eine weitere interessante Fakten über den großen Objekt-Heap ist, dass er den während des Sweep-Zyklus nicht verdichtet . Es ist einfach verschmilzt freie Speicherblöcke zusammen, aber es führt keine Komprimierung durch, um die Gesamtmenge des freien Speicherplatzes zu optimieren. Sie fragen sich vielleicht, was bestimmt, ob ein Objekt klein oder groß ist? Nun, die Größenschwelle liegt bei 85 Kilobyte. Alle Objekte mit 85 Kilobyte für größere werden direkt in den großen Objekt-Heap verschoben. Alle Objekte, die kleiner als diese Grenze sind, gehen in den kleinen Projekt-Heap. Diese zwei separaten Haufen zu haben, löst das Problem von großen, lang lebenden Objekten. Sie müssen nicht mehr viermal kopiert werden, bevor sie in der Generierung in landen, sondern gehen direkt in den großen Objekt-Heap , der nur in der Generation verarbeitet und nie komprimiert wird. Und da haben Sie es, der Hund Netze Müllsammler ist eine Generationen-Müllqualität oder das verwendet eine Marke Sweep kompakt Zyklus. Es hat separate Heaps für große Objekte und kleine Objekte. Wenn Sie darüber nachdenken, macht der DOT Net Garbage Collector einige sehr spezifische Annahmen über Objekte und Lebenszeiten. Erstens geht es davon aus, dass Objekte entweder kurze Lift oder lange Lebensdauer sein werden. Alle Short Lift-Objekte sollten in einem einzigen Sammlungszyklus zugewiesen, verwendet und verworfen werden. Jedes Objekt, das sozusagen durch die Risse rutscht , wird im nächsten Zyklus in Generation eins gefangen. So landet jedes Objekt, das zu Sammelzyklen überlebt, in Generation zu und muss ein langes lebendiges Objekt sein. Auch jedes Objekt mit einer Größe von mehr als 85 Kilobyte wird immer als ein lang lebendiges Objekt angesehen. Beim Betrachten der Sammlungsfrequenz von den verschiedenen Generationen ist klar, dass der Garbage Collector davon ausgeht, dass die überwiegende Mehrheit von Objekten kurzer Dauer sein wird. So kann ich meine Tipps zur Speicheroptimierung in einem einzigen Satz zusammenfassen. Gehe nicht gegen diese Annahmen. Also, was haben wir gelernt? Der Garbage Collector verwendet einen Markierungs-Sweep und einen kompakten Zyklus. Der Garbage Collector verfügt über zwei separate Heaps für große und kleine Objekte. Der große Objekt-Heap auf dem kleinen Ziel. Das kleine Objekt, das er drei Generationen verwendet, werden alle neuen Objekte in Generation Null zugeordnet und Fortschritt in Richtung Generation auf das große Objekt. Er hat eine einzige Generation, die zusammen mit Generation zu dem kleinen Ziel verarbeitet wird . Außerdem komprimiert der Heap des großen Objektes nicht den Heap. Um den freien Speicher zu optimieren, macht der Garbage Collector zwei wichtige Annahmen zu Objektgrößen und Lebenszeiten. Ein 90% Rabatt auf alle Objekte, die kleiner als 85 Kilobyte sind, muss für alle Objekte, die größer als 85 Kilobyte sind, eine kurze Lebensdauer haben. 17. Tipp Nr. 7: Optimiere für die Müllsammlung: Willkommen zu Teilen aus der Vortragsreihe über schnelle Garbage Collection. In diesem Vortrag werden wir uns mehrere Performance-Optimierung zu betrachten ist, um den Garbage Collector so schnell wie möglich laufen zu lassen. Aber zuerst, lassen Sie uns zusammenfassen, was wir über den Garbage Collector gelernt haben. In der vorherigen Vorlesung verwendet der DOT-Netz-Garbage Collector einen Markierungs-Sweep im kompakten Zyklus, um D referenzierte Objekte von der Hitze zu bereinigen . Es wird verwendet, Heaps für große und kleine Objekte zu trennen. Der große oder Steg auf diesem kleinen Objekt, die kleinen Objekte, die er verwendet. Drei Generationen, alle neuen Objekte werden in Generation Null Ons zugeordnet. Der Fortschritt hin zu Generation Null wird sehr häufig gesammelt. Generationen eine Hände zu viel weniger so. Generationen halfen, die Anzahl von Objekten in Generation Null zu begrenzen. Jeder Sammlungszyklus löscht die Generierung Null vollständig von allen Objekten im nächsten Zyklus , der Garbage Collector muss nur neue Objekte überprüfen, die nach dem letzten Zyklus erstellt wurden . Die erste speicherbasierte Leistungsoptimierung, die wir betrachten werden, besteht darin, einfach die Anzahl von Objekten zu begrenzen, die wir in Generation Null erstellen. Je weniger Objekte erstellt werden, desto weniger Arbeit muss der Garbage Collector erledigen, um den Heap zu bereinigen. Es gibt zwei Strategien, die Sie befolgen können, um die Anzahl von Objekten auf der Hüfte zu begrenzen. Die erste besteht darin, sicherzustellen, dass Ihre Codes keine redundanten Objekte irgendwo auf der zweiten Stelle erstellen , um Ihre Objekte so schnell wie möglich zuzuweisen , zu verwenden und zu verwerfen, damit sie bereits die vom Garbage Collector im nächsten Zyklus. Wenn Sie zu lange zwischen der Zuweisung und Verwendung beim Verwerfen Ihrer Objekte warten , laufen Sie das Risiko ab, in den Generationen ein oder zwei zu landen. Für kurzlebige Objekte möchten Sie also, dass Ihre Mäntel so eng wie möglich sind. Werfen wir einen Blick auf einige Beispiele. Hier ist ein Codefragment, das 10.000 Mal eine Zeichenfolge aufbaut, indem ein String-Builder mit einem kalten an die appends Methode verwendet wird. Kannst du das Problem mit diesem Mantel sehen? Ich gebe dir 10 Sekunden zum Nachdenken. Hier ist die Lösung. Das Problem ist mit der String-Verkettung innerhalb der appends Nachricht, Sie werden sich daran erinnern, dass Strings unveränderlich sind, und so erstellt die zwei String-Nachricht auf dem Addition beide zusätzliche String-Objekte auf dem Heap für jede Schleife, die sie oration die Kälte an der Unterseite vermeidet dieses Problem durch Montage und ruft zweimal nach oben. Der Unterschied ist 40.000 weniger String-Objekte auf dem Heap. Das ist eine große Verbesserung. Also hier ist ein weiteres Beispiel. Sehen Sie, ob Sie das Problem erkennen können. Ich gebe dir wieder 10 Sekunden. Und hier ist die Lösung. Wenn Sie Ganzzahlen in einer Array-Liste speichern, die ganzen Zahlen in der Hüfte boxed. Die generischen Listen vermeiden dieses Problem, indem sie auf internes ganzzahliges Array anstelle eines Objekt-Arrays verwenden, eine einfache Änderung der Codes, die dazu führt, dass 20.000 weniger in Ihre Objekte auf der OK eingehängt werden. Ein weiteres Beispiel. Ein kleines, statisches Objekt wird initialisiert, dann läuft eine Menge anderer Kult zuerst auf. Schließlich wird das Objekt tatsächlich verwendet. Was ist los mit diesem Bild? Ich gebe Ihnen 10 Sekunden, und hier ist die Antwort. Das Objekt ist klein, aber die Lücke zwischen Zuweisung und Verwendung ist sehr groß, so dass es eine große Chance gibt, dass die Objekte in der ersten oder zweiten Generation landen, bevor es verwendet wird. Die Codes am unteren Rand vermeiden dieses Problem, meine erste, meine erste, die Objekte dort nicht statisch machen, es ist kurz vor der Verwendung zuweisen und schließlich die Objektreferenz so einstellen, dass sie direkt nach dem Gebrauch wissen , um an den Müll zu signalisieren Kollektor, die auf, dass die Objekte fertig sind gesammelt werden. Wenn Sie nicht möchten, dass alle Ihre Codes keine Zuweisungen haben, können Sie auch die unteren Codes in der Nachricht umschließen, ebenso wie das Referenzobjekt beim Beenden der Methoden den Gültigkeitsbereich verlässt. Das ist meine Lieblingslösung. Die nächste Optimierung, die Sie durchführen können, besteht darin, die Lebensdauer Ihrer Objekte zu optimieren. Der Garbage Collector geht davon aus, dass das fast alles ist. Kleine Objekte werden kurzlebig sein, und alle großen Objekte werden lange heben, also sollten wir das Gegenteil vermeiden. Kleine, lange Lift, alle tex oder große kurz Motive. Es ist lehrreich, diese Kombinationen in einem Diagramm anzuzeigen. Wenn ich die Objektlebensdauer horizontal auf der Objektgröße vertikal plotten, bekomme ich die folgenden Diagramme. Die unteren linken und oberen rechten Quadranten sind dort, wo Sie sein möchten. Diese Kombinationen von Objekten, Größen und Lebenszeiten stimmen exakt mit den Annahmen aus dem Garbage Collector überein. Oben links unten rechts? Quadranten stehen im Widerspruch zu den Annahmen aus dem Garbage Collector. Wenn Ihr Code viele Objekte aus den Quadranten enthält, arbeiten Sie effektiv gegen die Annahmen aus dem Garbage Collector an der Leistung, die Ihr Mantel wahrscheinlich darunter leidet. Was können wir tun, um in die richtigen Quadranten zu gelangen? Beginnen wir mit Objekten. Lebenszeit zu Wieder Faktor große, kurze Lift-Objekte müssen wir das Objekt zu erhöhen. Lebenszeit. Dafür gibt es eine sehr einfache Strategie, die Objektpooling genannt wird. Die Idee ist, dass stattdessen oft verwerfen und Objekte und Zuweisung und neue Objekte. Stattdessen verwenden Sie die vorhandenen Objekte wieder. Da die gleichen Objekte immer wieder verwendet werden, wird es effektiv eine lange lebende Objekte. Dies ist eine sehr beliebte Strategie zur Optimierung des Heaps großer Objekte. Schauen wir uns ein Beispiel an. Hier ist ein Fragment von Codes, das eine große Array-Liste zuweist, sie dann zweimal verwendet Neuzuweisung der Liste zwischen den Verwendungen verworfen wird. Wie würdest du diesen Mantel verbessern? Ich gebe Ihnen 10 Sekunden, um darüber nachzudenken, und hier ist die Lösung. Stattdessen bei der Neuzuweisung der Liste verwerfen, löschen Sie sie stattdessen mit einem Aufruf an die klare Nachricht und verwenden Sie dann die Liste für die zweite Methode erneut , rufen Sie den neuen Mantel auf. Die gleichen Array-Listenobjekte werden immer und immer verwendet, seine Lebensdauer wird auf die Objekte erhöht effektiv lange Lebensdauer. Diese Änderung verbessert die Leistung und verringert die Wahrscheinlichkeit, dass der Heap für große Objekte fragmentiert wird . Sehen wir uns nun das umgekehrte Problem an. Wir haben eine kleine, lange heben Objekte, die wir brechen müssen oder in eine kurzlebige Objekte. Wie würde das funktionieren? Hier ist ein Beispiel. Dieser Mantel füllt eine Array-Liste mit 10.000 Paar-Objekten. Pfund jedes Haar enthält zwei in Ihrem. Also, was ist mit diesem Code falsch? Ich gebe dir 10 Sekunden, um darüber nachzudenken. Das Problem ist, dass die Array-Liste ein großes Objekt ist, also geht es weiter zu dem großen Objekt, das er voraussetzt, dass es lange Lebensdauer ist. Aber die Liste ist mit winzigen Paarobjekten gefüllt, 10.000 davon. Alle diese Objekte gehen, bis der kleine Objekt-Heap in Generation Null fällt, da die Array-Liste jedes Eisen einen Referenzfuß beibehält. All diese Eltern werden nie eine Referenz, und sie werden schließlich alle in eine Generation bewegen. Die Lösung besteht darin, stattdessen eine andere beliebte Refektorien-Strategie zu verwenden, wobei eine Liste mit zwei Ganzzahlen in jedem Listenelement vorhanden ist. Wir brechen die Liste der Teile in zwei separate inter generiert. Da eine Ganzzahl ein Werttyp ist, wird sie mit dem Array gespeichert. Also jetzt haben wir nur zwei größere Erhöhung in dem großen Objekt er und absolut nichts in Generation Null. Problem gelöst. Die dritte Optimierung, die Sie durchführen können, ist, Juni die Größe Ihrer Objekte zu finden. Der Garbage Collector geht davon aus, dass fast alle langsamen Objekte von kurzer Dauer sein werden. Alle großen Objekte werden lange Lebensdauer haben. Wenn wir also die Gegensätze in unseren Codes, Schul-Lang-Objekte oder große kurze linke Objekte haben, müssen wir die Größe von diesen Objekten abbrechen, um sie wieder in die richtige Ladung zu bringen. Übereinstimmung. Beginnen wir mit einem großen kurzen linken Objekt, um die Größe von diesem Objekt zu reduzieren. Es gibt zwei Strategien. Eine Teilung des Objekts von Teilen in Unterobjekten, jeweils kleiner als 85 Kilobyte oder zwei sind, reduzierte den Speicherbedarf vom Objekt. Hier ist ein Beispiel für die zweite Strategie. Eine Schleife füllt den Puffer mit 32 Tausend, aber es ist Kannst du sehen, was mit seinen Codes falsch ist? Ich gebe Ihnen 10 Sekunden, und hier ist die Antwort. Die Schleife füllt den Puffer mit Bits, aber der Puffer ist als Array aus ganzen Zahlen definiert. Der Puffer enthält 32.000 Elemente auf Da eine ganze Zahl vier Bytes groß ist, summiert sich dies bis zu 100 28 Tausend Bits. Dies ist über dem Schwellenwert für große Objekte, und daher geht dieser Puffer direkt, bis der große Objekt-Heap auf in der Generation zu der Lösung gesammelt wird , ist es, den Puffer neu zu faktorieren, wie er den Puffer beißt. Jetzt beträgt der Speicherbedarf aus dem Puffer genau 32.000 Bytes, was kleiner ist als die Schwellenwerte für große Objekte. Und so bekommt es Läden auf dem kleinen Ziel in Generation Null, genau wie wir waas. Sehen wir uns das umgekehrte Problem an. Wir haben ein kleines, langlebiges Objekt, das wir Faktor in eine große wieder müssen, lange Aufzüge Objekt. Wie würde das funktionieren? Die Lösung besteht darin, entweder den Speicherbedarf aus dem Objekt zu vergrößern oder mehrere Objekte miteinander zu verschmelzen , um größere Objekte zu erstellen, die auf das große Ziel gehen können. Also hier ist das letzte Beispiel aus dieser Vorlesung, dieser Mantel deklariert eine statische Array-Liste auf. Dann, irgendwo auf halbem Weg, beginnt das Programm, es zu benutzen. Was stimmt mit diesem Code nicht? Ich gebe dir 10 Sekunden. Hier ist die Antwort. Es ist klar, dass das Objekt ein lang lebendiges Objekt sein soll, weil es als statisch deklariert wird . Wenn wir auch wissen, dass die Liste irgendwann mindestens 85 Kilobyte Tage enthält, was? Dann ist es besser, die Liste auf diese Größe zu initialisieren. Dies stellt sicher, dass die Liste direkt auf den großen Projekt-Heap geht, denn wenn Sie die Liste nicht initialisieren, erhält sie die Standardkapazität, die aus dem oberen Teil meines Kopfes 16 Kilobyte beträgt. Dann geht die Liste in den kleinen Objekt-Heap in Generation Null und wechselt schließlich zur Generierung nach potenziell, nachdem sie vier Speicherkopieoperationen durchlaufen hat, indem sie die Liste sofort auf die richtige Größe initialisiert hat, vermeiden Sie die Migration von Generation Null zu Generation vollständig. Es mag seltsam erscheinen, dass Sie den Mantel optimieren können, indem Sie Objekte größer machen, aber genau das tun wir hier. Und manchmal funktioniert es wirklich. Was haben wir gelernt, Ihren Code so zu optimieren, dass der Garbage Collector so schnell wie möglich läuft ? Sie müssen diese Strategien zuerst befolgen, begrenzen Sie die Anzahl von Objekten, die Sie erstellen Sekunden zuweist Verwendung auf verwirft kleine Objekte so schnell wie möglich. Sewards Wiederverwendung aller großen Projekte. Sie möchten mit dem Garbage Collector und nicht dagegen arbeiten. Und so müssen Sie sicherstellen, dass alle Objekte in Ihren Codes entweder klein und kurzlebig sind. Vier große und lange Leben. Also, wenn Sie zufällig Objekte haben, die entweder üppig und kurze Lift oder klein auf langen Lift sind , Sie möchten vielleicht wieder Faktor sie für große kurz Themen. Sie können entweder den lebenslangen Krieg erhöhen, die Größe vom Objekt und vier kleine lange Liftobjekte verringern. Sie können entweder das Lebenszeichen verringern oder die Größe vergrößern. All diese Änderungen werden der Leistung Ihres Mantels zugute kommen. 18. Tipp Nr: 8: Benutze schnelle Teilnehmer:innen: in dieser Vorlesung werde ich einen genaueren Blick werfen. Das sind Delegierte. Nun, wenn Sie nicht mit dem Konzept von Delegaten vertraut sind, die Delegaten nichts anderes als ein Typ, der einen mythischen umschließt. Wo also ein regulärer Typ definiert, welche Art von Daten schwächen, speichern , sagen wir, streamen Sie in Ihre Daten usw., definiert ein Delegates, welche Art von Methode wir aufrufen können. Was ist die Methode für Amateure, was ist der Rückgabetyp zu definieren? Ein Delegat in C. Sharp, ich benutzte die Delegaten-Schlüsselwörter wie diese. In diesem speziellen Beispiel wird ein Methodenaufruf definiert, der erwartet, dass Ihre Eingabeparameter ein unsicherer Ausgabeparameter für einen void-Rückgabetyp abgesetzt werden. Nun definiert diese Deklaration noch keine Variablen. Alles, was wir an dieser Stelle haben, ist eine neue Typdefinition namens Adds Delegate, die eine bestimmte Methoden Signatur beschreibt, um eine neue Delegaten zu erstellen. Variabel. Ich muss das tun. Dadurch wird ein neuer variabler Off-Typ eingerichtet. Es sind Delegierte. Wie signiere ich den Wert für diese Variable? Nun, zuerst müssen wir irgendwo in unserem Code Methoden haben, die genau mit der Signatur übereinstimmen, die wir zuvor eingerichtet haben , um Ihre Eingabeparameter einzufügen. Ein ganzzahliger gibt Parameter aus und ein void Rückgabetyp etwa so. Jetzt, da ich die Methodenimplementierung habe, kann ich die Delikates-Variable so zuweisen und dann kann ich sie so nennen. Jetzt, was Sie bisher gesehen haben, ruft eine eindeutige Besetzung Delegierten. Dies ist ein Delegat, den ich mit einer einzigen Nachrichtenimplementierung initialisiert habe und wenn ich die Delegaten aufrufe, ruft er die einzelnen Methoden auf. Aber die Delegierten sind viel mächtiger als das. Ich kann auch ein Multi-Cast-Delegaten einrichten. Dies ist ein Delegaten, die mehr als eine Methode in einem einzigen Schritt aufrufen können. Nehmen wir an, ich muss Methoden in meinem Mantel hinzufügen, genannt Es ist warm und fügt hinzu. Ich kann dann die Delegierten so einrichten. Beachten Sie die Verwendung aus dem Plus ist Operator in der dritten Zeile. Dies, als zusätzliche Methode für einen vorhandenen Delegaten auf effektiv eine Multi Cast-Delegaten einrichten. Alle Delegaten können mehrfach geklärt werden, indem einfach weitere Methoden hinzugefügt werden. Es gibt keine Grenzen für die Anzahl Off-Methoden, die Sie fragen können, aber bitte gehen Sie nicht wieder über Bord. Ich rufe die Delicates so auf, dass alle Add-Methoden in der Reihenfolge in der gleichen Reihenfolge aufgerufen werden , wie ich sie zu den Delicates hinzugefügt habe. Das hat mich also gefragt. Gibt es einen Leistungsunterschied zwischen einer eindeutigen Besetzung und einem Multi-Cast-Delegaten? Auf? Was wäre die Strafe mit einem Delegaten anstelle eines regulären Methodenaufrufs deaktiviert? Lass es uns rausfinden. Um die Leistung von Delegaten zu testen, habe ich den folgenden Code geschrieben. Ich beginne hier mit den vertrauten Delegierten, um zwei Zahlen zusammen hinzuzufügen. Hier unten sind zwei Implementierungen von den Hands-Methoden, die vollständig identisch sind und mit der Delegates-Signatur übereinstimmen. Dann führe ich drei Tests in meinem ersten Test durch. Ich rufe die Anzeigenmethoden direkt auf. Dadurch wird eine Basislinie bereitgestellt. Der zweite Test richtet eine einzigartige Besetzung ein. Delegierte auf Anrufe es zweimal. Dies wird uns sagen, was der Overhead ausgeschaltet ist, indem die Delegaten stattdessen direkt die Anzeigenmethoden aufrufen. Und schließlich habe ich mehrere benutzerdefinierte Delegaten abgesandt, sie mit zwei Methoden zuweisen und dann das heikle aufzurufen. Einmal. Dies wird die Leistung von Multicast auf eindeutigen Besetzungsdelegaten vergleichen, und hier sind die Ergebnisse. Es gibt einen Leistungsaufwand von 9%, wenn Sie einen Delegaten verwenden, anstatt den Handler direkt aufzurufen , das ist nicht zu schlecht, aber die Verwendung eines Multicast-Delegaten ist mehr als doppelt so langsam wie ein eindeutiges als Delegaten zweimal aufzurufen . Whoa. Der Grund dafür liegt darin, wie Delikate umgesetzt werden. Hinter den Kulissen wird ein Delegates von der Multi Cast Delegates Klasse implementiert. Diese Klasse ist für uni-cast-Delegaten optimiert. Er verwendet eine Nachricht für eine Zieleigenschaft, um direkt eine einzelne Methode aufzurufen. Aber für Multicast-Delegaten verwendet die Klasse eine Aufrufliste für interne generische Listen, hält Verweise auf jede Methode, und sie werden in der Reihenfolge der Gemeinkosten aus aufgerufen. Das Durchlaufen der Aufrufliste ist, was diesen großen Leistungsunterschied verursacht. Abschließend möchte ich Ihnen einen Rat geben. Ich sehe manchmal Kälte so. Diese Codezeile definiert eine Demagogue-Variable auf, dann initialisieren. Ist es mit einem Doubie-Methoden, die nichts tut. Der Vorteil vor der Initialisierung der delikaten Variablen ist, dass wir nicht mehr überprüfen müssen ob die Variable nein ist. Anstatt dies zu tun, können wir dies jetzt einfach tun, unabhängig davon. Wenn die Delegaten initialisiert wurden, funktioniert der Code immer. Die Delegierten rufen entweder die Dummy-Nachricht auf nichts tun oder rufen die Dummy-Chaos auf Duthie Zeichen Nachricht in der Reihenfolge. Aber bitte tun Sie das nicht. Sie haben in den Leistungsmessungen gesehen, dass Multicastdelegaten viel langsamer sind als eindeutig als Delegaten. Die einfache Aktion vor der Initialisierung der Delegate-Variable macht den Mantel also mehr als doppelt so langsam. Also hier ist mein Rat zu schnellen Delegierten in C scharf. Der Performance-Overhead bei Verwendung der Delegaten beträgt nur 9%. In den meisten Fällen ist dies durchaus akzeptabel, und so fühlen Sie sich frei, Delegierte überall in Ihrem Mantel zu verwenden, wo es bequem ist Breath, wenn Sie die höchstmögliche Leistung haben müssen, dann entfernen Sie alle Delegierten von geschäftskritische kalte Abschnitte und schnappen Sie sich diese zusätzlichen 9% Rabatt auf die Und Sie sollten immer vermeiden, Multicast-Delegaten in Mission Critical Coast zu verwenden, da sie mehr als doppelt so langsam sind, so einzigartig wie Delegierte. 19. Tipp Nr. 9: Eine schnelle a aufbauen: In diesem Vortrag werde ich sehen, ob ich einen sehr häufigen Mantel beschleunigen kann. Konstruieren Sie die Klassenfabrik. Jetzt fragen Sie sich vielleicht, was genau ist eine Klassenfabrik? Einfach gesagt, eine Klassenfabrik ist eine spezielle Klasse, die andere Klassen nach Bedarf basierend auf irgendeiner Art externer Konfigurationsdaten konstruiert . Häufig sehen Sie Klassenfaktorien, die beim Zugriff auf Datenbanken in Codes verwendet werden. Wir wollen einfach auf die Datenbank zugreifen. Uns ist es egal, ob die Daten in einer Microsoft Sequel-Server-Datenbank in Oracle-Datenbank oder Maya Scwill Datenbank gespeichert werden. Das ist ein Implementierungsdetail, über das sich die Codes keine Sorgen machen müssen. In Codes sehen Sie vielleicht so etwas. In diesem Beispiel ist The Class Connection Factory eine Klassenfactory, die weiß, wie ein Datenbankverbindungsobjekt für die Verkaufsdaten erstellt wird. Wenn es also irgendwo in einer Konfigurationsdatei eine Einstellung gibt, die angibt, dass alle Verkaufsdaten in einer Artikeldatenbank gespeichert sind, kann die Klassenfactory diese Informationen verwenden, um einen Systempunktdatenpunkt zurückzugeben. Oracle-Clients punktet Oracle Connection Klasse. Sie können sehen, wie Klasse Fabriken, einfach fünf die Kälte enorm. Sie müssen sich nicht mehr darum kümmern, wo bestimmte Daten gespeichert sind. Die Klassenfabrik weiß on gibt automatisch die richtigen Verbindungsobjekte für jede Anforderung zurück . Klassenfabriken haben einen weiteren großen Vorteil. Sie ermöglichen es uns, Daten zur Laufzeit zu verschieben, wenn zueinem bestimmten Zeitpunkt einem bestimmten Zeitpunkt die Verkaufsdaten in eine meine SQL-Datenbank migriert werden. Alles, was wir tun müssen, ist die Konfigurationsdatei bei der nächsten Datenanforderung zu aktualisieren, wird die Klassenfabrik Lesen Sie die geänderten Konfigurationsdaten und geben Sie stattdessen A My SQL-Verbindungsobjekte zurück Um eine Klassenfactory zu erstellen, müssen Sie dieses Fragment off-coat vervollständigen. Die Eingaben sind also eine Zeichenfolge, die den Typ von der Klasse enthält, die sein soll. Stattdessen ist sie auf die gewünschte Ausgabe jetzt eine tatsächliche Instanz aus dieser Klasse, das ist eigentlich ein schwieriges Problem zu lösen. Die nächste, die Sie bekommen können, ist kompiliert. Die Zeit ist so etwas. Dies funktioniert wirklich gut, weil der Compiler jedes mögliche Szenario kompilieren kann, auf das wir zur Laufzeit stoßen könnten . Aber der Nachteil ist, dass wir vorher jede mögliche Nutzung aus der Klassenfabrik antizipieren müssen. Zum Beispiel ist es völlig unmöglich, die Verkaufsdaten in eine meine SQL-Datenbank zu verschieben, wenn wir die Verwendung von meinem SQL mit entsprechenden Fallanweisungen vor Händen nicht erwartet haben . Daher ist dieser Mantel in einer Klassenfabrik nicht sehr nützlich, aber er hat die bestmögliche Leistung. Ich werde den Code später zu meiner Messung hinzufügen, um als Benchmarkwert zu dienen. Okay, also wird ein Switch-Anweisungen nicht ausreichen. Was haben wir sonst noch? Eine andere Möglichkeit besteht darin, Reflexion zu verwenden. Das System erwachsene Reflexion, Namensraum hat eine sehr schöne Klasse namens Aktivator, die wir verwenden können, um jede Art von Objekten zu konstruieren , die wir basierend auf dem Namen seines Typs mögen. So kann ich die vorherige Methode neu schreiben, um stattdessen Reflexion zu verwenden, und es wird ungefähr so aussehen. Jetzt wird das in allen Szenarien perfekt funktionieren, ich kann jeden Typnamen eingeben, den ich mag. Wenn der Typ existiert, konstruiert die Aktivator-Klasse die entsprechende Klasseninstanz. Es ist perfekt. jedoch Diese Erkältung hatjedochein großes Problem. Die Reflexion ist wirklich langsam. Wir werden sehen, wie langsam in einem Moment, wenn ich den Benchmark führe, aber vertrauen Sie mir, wenn ich sage, dass dies einen massiven Performance-Rückschlag in Ihrem Mantel einführen wird, muss dies nicht immer ein Problem sein. Wir können davon ausgehen, dass Datenbankverbindungen nicht allzu oft erstellt werden. Darüber hinaus ist das Öffnen einer Datenbankverbindung eine viel langsamere Operation als das Erstellen der Klasse, so dass eine Verlangsamung der Konstruktion für den Endbenutzer kaum spürbar ist. diesem Grund sehen Sie oft, dass die Aktivatorklasse in C-scharfem Fell in nicht kritischem Fell auftaucht. Es ist absolut nichts falsch daran, ein wenig Reflexion zu verwenden, um Ihren Bedürfnissen gerecht zu werden. Aber was ist mit kritischer Kälte, wo Spitzenleistung entscheidend ist? Glücklicherweise gibt es eine andere Lösung. Ich werde Ihnen einen sinnvollen Trick zeigen, um jede Art von Klasse zur Laufzeit mit einer Leistung zu konstruieren , die mit den switch-Anweisungen vergleichbar ist. Sieh dir das an. zunächst Lassen Sie unszunächstauf das grundlegende Problem zurückkommen, das wir brauchen, um diese Methode abzuschließen. Dies ist eine genetische Methoden, die jederzeit konstruieren können, aber eine Sammlung von bestimmten Methoden wäre auch in Ordnung. Nehmen wir an, ich muss eine Klasse konstruieren, die meinen Klassentyp 1 genannt wird. Dann würde ich die folgende Nachricht in der gemeinsamen Zwischensprache benötigen. Der Körper von der Nachricht wird so aussehen. Also hier ist der Gast aus dem Trick. Ich werde dynamisch einen Delegaten zur Laufzeit mit diesen beiden Zwischensprachenanweisungen erstellen und dann die Delegaten aufrufen, um die Klasseninstanz zu erstellen. Hier ist, wie es funktioniert. Meine Klassenfabrik erhält zuerst die Typnamenzeichenfolge, die den Typ von der zu erstellenden Klasseninstanz beschreibt. Das erste, was die Klassenfactory tut, ist, ein Wörterbuch einzuchecken, ob die entsprechenden Delegaten bereits erstellt wurden. Wenn dies der Fall ist, ruft die Factory die Delegaten aus dem Wörterbuch ab und ruft sie direkt auf, um die Klasse zu erstellen . Instanz weiß sogar, dass die Factory eine dynamische Methode erstellt, um die neuen Objekte zu schreiben und Anweisungen in sie zurückzugeben. Es wickelt dann die Methoden in einen Delegaten und speichert die Delegaten im Wörterbuch für den Fall wir es später wieder benötigen. Schließlich ruft es die Delegaten auf, um die Klasseninstanz zu erstellen. Erstellen einer dynamischen Methoden Delikate ist wirklich langsam, sogar langsamer als das Aufrufen der Aktivator-Klasse. Aber hier ist die Sache. Sobald wir einen Delegaten haben, können wir ihn immer und immer wieder aufrufen, um mehr Klasseninstanzen zu erstellen. Andi, einen Delegaten aufzurufen, ist viel schneller als die Verwendung der Aktivator-Klasse, so dass es nur eine Verzögerung geben wird, wenn die ersten Objekte von einem bestimmten Typ erstellt werden. Jede nachfolgende Kohle wird super schnell sein. Okay, also schauen wir uns mein Benchmarking-Programm für diesen Vortrag an. Sie können in dieser ständigen Erklärung hier oben sehen, dass ich mich interessieren werde, und sie hat eine Million Objekte gegessen. Die erste Message Measure a loops eine Million Mal auf verwendet die statischen switch-Anweisungen, um die Objekte zu konstruieren, die Sie hier sehen können. Dass mein Programm String-Builder-Instanzen auf erstellt, unterstützt nichts anderes. Die switch-Anweisungen sind also ein bisschen albern, da sie nur eine einzelne Klasse unterstützt, die nach der Kopulation in keiner Weise geändert werden kann . Aber wir werden es hier behalten, um als Benchmark-Wert zu dienen. Dies bedeutet die bestmögliche Leistung beim Erstellen von einer Million Objekten. Als nächstes ist Measure B, die die Aktivator-Klasse verwendet, um die String-Builder wirklich einfache und saubere Codes zu erstellen . Es ist wirklich eine Schande, dass es nicht sehr gut funktionieren wird. Sie werden in einem Moment sehen, wie langsam Reflexion tatsächlich ist. Schließlich ist hier Measure C Es verwendet eine Klassenfabrik namens bekommt Klassen-Schöpfer. Diese Klassenfactory gibt einen Klassenersteller zurück , den Sie sehen können, ist eigentlich ein Delegat für eine Methode ohne Parameter, die kein Objekt zurückgibt . Die nächste Zeile erstellt diese String-Builder, indem einfach die Delegaten aufgerufen werden. Also passiert die ganze Magie in der Klassenfabrik. Das erste, was die Klassenfabrik tut, ist, die Delegaten aus dem Wörterbuch abzurufen . Wenn das Wörterbuch die Delegaten noch nicht enthält, ruft die Factory die Konstruktorinfo für den String-Builder ab. Es erstellt dann eine dynamische Methode. Dan's schreibt das neue Objekt bei Rückgabeanweisungen in die Methode. Schließlich wickelt es die Methoden in einem Delegaten in Stores. Die Delegaten im Wörterbuch schließlich hier unten die Hauptprogrammmethoden rufen alle drei Measuremethoden auf und zeigen die eigene Zeit in Millisekunden an. Okay, mal sehen, wie sich diese drei Techniken messen. Ich werde das Programm ausführen. Schauen Sie sich das an und hier sind die Ergebnisse. Die switch-Anweisung lief in 131 Millisekunden. Die Aktivator-Klasse sprechen 11.377 Millisekunden auf der dynamischen Methode sprechen 673 Millisekunden. Hier sind die Ergebnisse in einem Diagramm. Die switch-Anweisung konstruierte also eine Million String-Builder in 131 Millisekunden. Dies entspricht 7633 Objekten erstellt Paar Millisekunden, aber die Aktivator-Klasse hat viel, viel schlimmer. Es dauerte satte 11.377 Millisekunden, um die String-Builder zu konstruieren, was nur 87 Objekten für Millisekunden entspricht. Die Aktivator-Klasse ist 86 mal langsamer als die switch-Anweisungen. Und schließlich, hier sind die dynamischen Nachrichtenergebnisse. Die dynamischen Methoden Delegate konstruierten eine Million String-Builder in 673 Millisekunden. Dies entspricht 1485 Objekten für Millisekunden, so dass die dynamischen Methoden fünfmal langsamer sind als die switch-Anweisungen. Aber es ist fast 17 mal schneller als die Aktivator-Klasse. Okay, also was haben wir gelernt? Eine Class-Factory ist eine Klasse, die andere Klasseninstanzen nach Bedarf unter Verwendung externer Konfigurationsinformationen konstruiert . Eine übliche Möglichkeit, Klassenfabriken zu implementieren, ist mit der Aktivator-Klasse, aber die Aktivator-Klasse ist 86 mal langsamer als statisch kompiliert. Mantel Eine viel bessere Lösung ist die Verwendung dynamischer Methoden, delegiert die dynamischen Methoden. Delegaten sind nur fünfmal langsamer als statische Kompilierungscodes auf 17 mal schneller als die Aktivator-Klasse. Mit anderen Worten, wenn Sie die Aktivatorklasse durch dynamische Nachrichtendelegaten in Ihrer Klassenfabrik ersetzen, werden Sie Ihren Mantel nicht um einen Faktor von 17 beschleunigen. 20. Sind die Probleme auf dem Stapel wert?: In den letzten Vorlesungen haben wir uns Sammlungsklassen angesehen, einschließlich der Array-Liste, der generischen Liste über verschiedene Arten von nativen Raise, eindimensionale, zweidimensionale und gezackte Arrays. Es gibt eine Sache, die alle diese Sammlungsklassen gemeinsam haben. Sie leben alle auf dem Haufen. Aber was, wenn ich dir sage, dass es tatsächlich in C möglich ist? Scharfe Zehe weist auf Array von Interviewern direkt auf dem Stapel zu, wobei eine Sammlung von ganzen Zahlen aus dem Stapel als seine Vorteile hat. Wenn Sie sich den Heap ansehen, ist der Heap verwalteter Speicher. Dies bedeutet, dass der Garbage Collector gelegentlich Speicherblöcke um die Zehe bewegt, um freien Speicherplatz zu optimieren. Aber dieser Stack funktioniert anders. Speicher auf dem Stapel wird zugewiesen, wenn Ihr Code eine Methode als D zugewiesen eingibt. Wenn Sie von einer zugewiesenen Methode zurückkehren, wird der Speicher nie verschoben. Dies hat einen wichtigen Vorteil. Wenn wir irgendwie einen Zeiger auf den Stack-Speicher bekommen könnten, bleibt er gültig, bis der Stack-Speicher der zugewiesene ist. Es ist jedoch viel schwieriger, einen Zeiger auf den Wärmespeicher zu bekommen, da der Garbage Collector den Speicher zu einem beliebigen Zeitpunkt irgendwo anders verschieben kann, um einen Punkt oder zwei Wärmespeicher zu erhalten , mit dem ein Block-Off-Speicher auf dem Heap fixiert werden muss, dem Garbage Collector sagen Sie können diesen blockierten Speicher nicht berühren, bis ich damit fertig bin All diese Speicherverwaltung erzeugt einen Performance-Overhead, daher ist es nicht unvernünftig zu erwarten, dass der Punkt der Operationen auf dem Stack schneller als den gleichen Punkt der Operationen auf der Also schauen wir uns noch einmal etwas Code an. Hier ist das Programm, das ich zuvor verwendet habe, um die Leistung aus der Array-Liste der generischen Liste auf dem nativen Array zu vergleichen . Aber ich habe eine weitere Testmethode an der unteren Maßnahme D hinzugefügt Werfen wir einen Blick. Ich beginne damit, unsichere Codes zu deklarieren. Block in C. Sharp Point of Operations sind nur innerhalb unsicherer Blöcke möglich, so dass dies ein obligatorischer Schritt ist. Dann verwende ich diese Schlüsselwörter stecken Ein Blick, um 10.000 ganze Zahlen direkt auf dem Stapel zuzuweisen . Der Rückgabewert aus diesem Vorgang ist auf Interviewer Pointer, der zeigt und direkt in den Stack-Speicher. Dann zwei verschachtelte lose. Die äußere Schleife wiederholt die Experimente 10.000 Mal. Der innere Look durchläuft alle 10.000 Elemente aus dem Array und Wissenschaft jedes einzelnen von ihnen. Sie sehen, dass, obwohl die Liste ein Interviewer ist, Pointer, die Syntax, Zehenzugriff und die Elemente genau die gleiche ist, als ob die Liste ein ganzzahliges Array wäre. Die eckigen Klammern löschen. Intacs funktioniert immer noch hier. Hier oben sind die Measure C-Methoden. Diese Methode führt genau die gleiche Operation aus, aber es verwendet ein reguläres ganzzahliges Array auf dem Heap, so dass wir die Leistung vergleichen können. Lassen Sie uns die Codes laufen. Los geht's. Die reguläre Ganzzahl von Ray dauert 808 Millisekunden, aber das Stack-Array benötigt nur 764 Millisekunden. Das Stack-Array ist fünf Punkte, 4% schneller. Werfen wir einen Blick hinter die Kulissen und sehen, was in der Zwischensprache vor sich geht. Ich werde die Umgebungen auf den Debug-Modus Mittelbruchpunkte umschalten, den Code erneut ausführen und dann zur Disassemblierungsansicht wechseln, damit wir die Zwischensprachenanweisungen auf sehen können . Und hier sind die Codes für die regulären Integer-Array-Zuweisungen. Sie sehen, dass dies die vertraute Anordnung von drei Laststandort-Anweisungen und einem Set-Element ist. Die Listenvariable wird an einem variablen Speicherort gespeichert. Eins auf wird auf den Auswertungsstapel geladen, dann wird der I Valuable zweimal geladen, einmal als Array-Index und einmal als der zu setzende Wert. Schließlich speichert die Konstruktion der Set-Elemente den Wert im gegebenen Index. Sehen wir uns nun die Poynter-Operation in verwalteten Codes an. Wenn ich scrolle, hier sind die gleichen Zuweisungen in den Measure D-Methoden. Zuerst wird der Listenzeiger auf den Auswertungsstapel geladen, dann wird die Variable I auf der Nummer vier geladen, multipliziert und zum Listenwert hinzugefügt. Dies ist sinnvoll, wenn Sie bedenken, dass eine Ganzzahl vier Bits im Speicher ist, der Mantel einfach die Speicheradresse berechnet, in die geschrieben werden soll, was Liste plus warum multipliziert mit vier ist. Die nächste Anweisung ist Load Location 3, die den Wert lädt, der auf den Auswertungsstapel geschrieben werden soll. Und schließlich haben wir einen Laden in direkter Anleitung dazu. Anweisung schreibt den Wert direkt in den Speicher. Also, vielleicht überraschend, diese sieben Punkte oder Anweisungen tatsächlich schneller ausgeführt werden als die vier Array-Anweisungen in der Maßnahme. C-Methode für den Unterschied besteht darin, dass die set-Elemente Anweisungsrechte in verwalteten Speicher, während der Speicher in direkter Anweisung direkt in eine feste Speicheradresse schreibt, ohne irgendwelche Arten von Speicherverwaltung nehmen zu müssen zu berücksichtigen. abschließend Lassen Sie michabschließenddas Programm noch einmal ausführen. Aber dieses Mal, mit allen Messungen, Methoden ermöglicht, werde ich die Array-Liste, die genetische Liste, das native Interview-Array, Hände der Ganzzahl oder eine Allokatoren auf dem Stapel. Los geht's. Und hier sind die Ergebnisse. Lassen Sie mich Ihnen eine Grafik aus den Ergebnissen zeigen, damit sie leicht zu vergleichen sind. Die Array-Liste dauert 9314 Millisekunden. Die generische Liste ist viel schneller. Das sind 2704 Millisekunden. Das ganzzahlige Array ist noch schneller. Das sind nur 745 Millisekunden. Aber dieses Stack-Array schlägt jeden mit einer Laufzeit von nur sieben Hunderten und 13 Millisekunden. Der Leistungsunterschied zwischen dem nativen Integer-Array auf dem Array auf dem Stack beträgt 4%. Dies ist viel geringer als der Unterschied zwischen, sagen wir, dem Integer-Array in der generischen Liste, die 72% ist. Wenn Sie also einige Codes optimieren, indem Sie eine genetische Liste durch das native Array ersetzen, das Ihnen bereits eine 70% ige Leistungssteigerung bietet, lohnt es sich, noch weiter zu gehen und das Array nur für den zusätzlichen Gewinn in ein Stack-basiertes Array zu verwandeln von nur 4 bis 5%. Die Antwort ist wahrscheinlich nicht. Der Leistungsunterschied ist so klein, dass es leicht ein Messfluke sein könnte. Um dieses Szenario zu testen, habe ich einige Änderungen an meinem Programm vorgenommen. Werfen wir einen Blick. Ich finde einfach das Programm, um nur das reguläre Array auf dem Stack zu betrachten, das jede Messmethode Parameter erwartet, der die Anzahl von Array-Elementen angibt, die zuzuteilen sind. Die Messung wird 5000 Mal bei jeder Methode wiederholt und kehrt zur Gesamtlaufzeit in Millisekunden zurück. Hier unten sind die wichtigsten Programmmethoden, mit der Schleife, die das Experiment für Differenzzahlen von Array-Elementen wiederholt. Also, was sind die Ergebnisse? Ich bereitete tatsächlich die Ergebnisse im Voraus auf legte sie in einem Diagramm vor. Also hier ist die Leistung für beide Arten des Rennens für die Renngrößen von 0 bis 49 Elemente. Ziemlich unschlüssig, würdest du sagen? Aber vielleicht wird der Leistungsunterschied als größere Array-Größen Bedeutung. Also lassen Sie uns das auch testen . Hier sind die Ergebnisse wieder, aber jetzt, für ein Rennen Größen zwischen tausend auf 100.000 Elemente wieder nicht schlüssig. Es gibt keinen eindeutigen Gewinner. Also, wann sollten Sie Stack basierend auf Rennen verwenden? Die Antwort ist fast nie, sicherlich nicht für die Leistungsoptimierung, die Verbesserungen sind winzig, nur ein paar Prozent, und oft ist das Stack-basierte Array tatsächlich langsamer als ein normales hitzebasiertes Array. Eso. Der Stapel ist in der Größe begrenzt. Stack-basierte Arrays können nur bis zu 10 Megabyte Daten speichern, bevor die Stacks Base ausgeht . Um Ihnen eine Idee zu geben, habe ich versucht, 10 Millionen ganze Zahlen auf dem Stapel zuzuweisen. Ich habe eine Stack-Überlauf-Ausnahme. Also fragen Sie sich vielleicht, warum ist das ein Schloss gestapelt? Schlüsselwörter sogar verfügbar? Nun, die Antwort ist, eine Schnittstelle zu anderen Programmiersprachen bereitzustellen, die Arrays auf dem Stapel zuweist . Wenn Sie Seashore Mantel schreiben, die mit dem Windows ein P I oder Bibliotheken geschrieben in C oder C plus, plusdie Aktien Schnittstelle Bibliotheken geschrieben in C oder C plus, plus hat. Alex Keywords können Sie einen Speicherpuffer als eine Art Gateway zwischen diesen Sprachen im Punktnetz verwalten Speicher einrichten. Also, zum Schluss, verwenden Sie nur ein Schloss für die Verbindung mit nicht verwaltetem Mantel auf. Verwenden Sie es nicht, um ein Array zu optimieren, da es sich wahrscheinlich nicht lohnt. 21. Wie verwende ich Zeiger in C#?: in diesem Vortrag werde ich jetzt über Zeiger sprechen. Zeiger werden nicht sehr viel verwendet, aber die C Sharp eine Sprache unterstützt sie, aber standardmäßig sind Point of Operations verboten, weil sie potenziell gefährlich sind. Wenn Sie Zeiger in Ihrem Mantel verwenden, kann die Donut-Laufzeit nicht mehr garantieren, dass der Speicher nie beschädigt wird. Und deshalb sind sie standardmäßig verboten. Punktoperationen werden jedoch verfügbar. Wenn Sie in Ihre Projekteigenschaften auf aktivieren unsafe coat gehen, dann können Sie die folgenden Deklarationen und Anweisungen in Ihrem Mantel verwenden. Die unsicheren Schlüsselwörter ermöglichen Point of Operations. Sie können die Schlüsselwörter verwenden, um Blöcke aus Mantel zu markieren, die Zeiger verwenden. Oder Sie können die Schlüsselwörter in eine Methodendeklaration einfügen, um diese ganzen Methoden unsicher zu machen. Wenn Sie eine Variable durch Hinzufügen und Asterix am Ende des Typs deklarieren, deklarieren Sie einen Zeiger von dieser angegebenen Zeit. In diesem Fall deklariere ich einen Bisspunkt oder die festen Schlüsselwörter, Pins und Objekte im Speicher und sage dem Garbage Collector, die Objekte nicht auf der Hitze zu bewegen, bis wir damit fertig sind . Da das Objekt fixiert ist, können Sie dann den Operator eines Prozentsatzes verwenden, um die Speicheradresse von den Objekten abzurufen. In diesem Beispiel fixiere ich Objekte in der Variablen Q auf der Hitze, und dann erhalte ich seine Adresse mit dem Operator und Prozent, und dann ein Zeichen, dass die Speicheradresse an den bys Zeiger in der Variablen p, die Asterix Operator de referenziert Terminer, was bedeutet, auf die Daten als Speicherort zuzugreifen. In diesem Beispiel lese ich eine einzelne Bisse an der aktuellen Adresse vom Zeiger. Sie können Zeiger auch wie ein Rennen behandeln. Der Indexervorgang liest Daten bei auf Offsets an die Current-Pointer Adresse. In diesem Beispiel lese ich eine einzelne Bisse am Speicherort. Zwei Bisse vor der aktuellen Adresse abseits des Punktes. Oder Sie können die Speicheradresse vom Zeiger ändern, indem Sie einfach dessen hinzufügen oder subtrahieren . In diesem Beispiel aktualisiere ich die aktuelle Speicheradresse vom Zeiger, indem ich sie drei Bits weiter vorantrete. Aus drei Gründen wurde der dominanten Laufzeit ein Punkt der Unterstützung hinzugefügt. Ein zwei unterstützt die Interoperabilität mit nicht verwaltetem Code, um ein C oder C plus zu unterstützen. Plus kompilieren Sie es nicht und drei, um es einfach zu machen Punkt basierter Algorithmen scharf zu sehen. Also sollten Zeiger in C Sharp verwendet werden oder einfach nur bei einem Rennen bleiben. Nun, lass es uns herausfinden. Das klassische Beispiel für die Verwendung von Zeigern in C. Sharp ist die Bildbearbeitung. Bilder, die in den Speicher geladen werden, werden vom Betriebssystem verwaltet. Die Bilddaten befinden sich im nicht verwalteten Speicher, da sie sich nicht auf dem Dunkelheits-Heap befinden. Es gibt also nur zwei Möglichkeiten, mit den Bilddaten zu interagieren. Sie verwenden High-Level Pixelstädte, setzen Pixelmethoden oder erhalten einen Zeiger auf den nicht verwalteten Speicher. Ich habe ein kleines Programm geschrieben, das Bild in den Speicher lädt und dann eine Graustufen-Transformation auf seiner durchführt Lassen Sie uns einen Blick werfen. Ich beginne mit den wichtigsten Methoden hier unten. Mein Testbild ist ein Bild von 1970er Jahren Modellbahn, behauptet Burke. Dieses Bild ist der Goldstandard zum Testen von Bildverarbeitungsalgorithmen. Ich liebe das Bild in den Speicher, dann als die Maßnahme bezeichnet, die ein Durcheinander besteht darin, die Graustufenkonvertierung mit den get Pixel durchzuführen und Excel-Methoden festzulegen und dann die Ergebnisse zurück auf die Festplatte zu schreiben. Dann mache ich das Ganze wieder, aber dieses Mal rufe ich die Maßnahme Be Message auf, die Point of Operations verwendet, um die Graustufenkonvertierung durchzuführen. Hier sind die Konvertierungsmethoden. Measure A empfängt das Bild als Parameter Schleifen durch jedes Pixel. Mit diesen 24 verlieren gegen die Farbe aus dem Pixel mit einem get Pixel medizinische. Die Formel führt hier die Umwandlung in Graustufen durch. Dann erstelle ich einen neuen Farbwert, indem ich die Graustufenvariable verwende und das Strömungspixel ändere, indem ich Seth Pixel aufrufe. Vergleichen Sie das nun mit Measure B. Ich beginne mit einem Aufruf, Bits zu sperren, die den Zugriff auf den Rohbilddater anfordert. Dann, in diesem unsicheren Autoblock, erhielt ich einen Biss-Zeiger auf die Bilddaten, indem ich die beiden Zeigermethoden aufrief. Die Graustufenumwandlungsformel ist hier bemerkt, dass ich einen Indexraum verwende, um die roten grünen und blauen Werte an der aktuellen Punktadresse auf und ein und zwei Bisse vor der aktuellen Adresse zu erhalten. Diese nächste Codesperre schreibt den Graustufenwert in die roten, grünen und blauen Pixelwerte, indem wiederholt an den aktuellen Adresspunkt geschrieben wird, wenn der Zeiger um einen Biss vorwärts bewegt wird. Und schließlich entsperre ich die Bilddaten bei der Rückkehr der Laufzeit in Millisekunden. Also, welche Methode wird schneller sein, denken Sie? Lassen Sie mich das Programm starten und es jetzt herausfinden. Wie ich bereits sagte, ich führe ein Screen-Capture-Programm gerade jetzt auf diesem Programm Druck auf die CPU auf verfügbaren freien Speicher. Auf diese verzerrt die Leistungswerte. Also habe ich diesen Abschnitt im Voraus vorbereitet und die unverzerrten Leistungswerte in ein Diagramm eingefügt. Hier sind die Ergebnisse, die ich den Test dreimal als Laufzeitumgebung für die Durchführung einer Graustufenkonvertierung ausführe. Verwendung von get pixel und set pixel liegt zwischen 146 auf 184 Millisekunden. Bei Verwendung von Zeigern sinkt die Laufzeit jedoch auf nur sieben Millisekunden. Bei allen drei Tests ist der Einsatzpunkt eine überwältigende 96% schneller als die Pixelmethode „get pixel“ . Abschließend möchte ich Ihnen die Ergebnisse zeigen. Ich werde einen Datei-Browser öffnen, wenn Sie das Originalbild auswählen und das Bild sieht so aus. Die Ausgänge aus Maß A und Maßnahme B sind hier. Wie Sie sehen können, ist es eine perfekte Graustufenkonvertierung auf die Ausgänge aus. Beide Methoden sind identisch. Also haben wir uns die Bildmanipulation in C scharf angesehen und festgestellt, dass Zeiger eine enorme Leistungssteigerung von 96% bieten. Ist es akzeptabel, Zeiger zu verwenden? In diesem Szenario lautet die Antwort ja. Das Hauptmitnehmen für diese Vorlesung wird immer verwendet. Zeiger für Bildmanipulation im Punktnetz. Sie haben keine Wahl, da sich die Bilddaten im nicht verwalteten Speicher befinden. Auf der Alternative get pixel und set pixel Methoden sind viel zu langsam. Ich kann diesen Rat in die folgenden Richtlinien verallgemeinern. Erstens, wenn Sie mit Daten in nicht verwaltetem Speicher interagieren, versuchen Sie, High-Level-get and set-Methoden zum Lesen und Schreiben der Daten zu verwenden. Dadurch vermeiden Sie, dass Sie die unsicheren Schlüsselwörter auf Ihrer Assembly als unsicher markieren verwenden müssen. Wenn diese Route nicht akzeptabel ist, weil Sie eine große Menge an Daten auf die Überköpfe lesen und schreiben müssen , wird die einzelnen get and set Methoden zu groß. Zen. Erhalten Sie stattdessen einen Zeiger auf die Daten, wenn Sie den Speicher direkt für die Bildbearbeitung ändern . Verwenden Sie immer Zeiger, weil die Bildtage, die ich immer zu groß für alle anderen Arten von High-Level-Zugriff sein werde. Es ist perfekt akzeptierte Praxis für Bildmanipulation. Bibliotheken, die in C scharf geschrieben wurden, um unsicher zu sein 22. Tipp Nr. 10: Beschleunige Code mit Zeigern: In der vorherigen Vorlesung haben wir uns die Verwendung von Zeigern für Bildmanipulation in C scharf angesehen. Jetzt qualifiziert sich Bildmanipulation als Szenario, in dem wir mit nicht verwaltetem Code interagieren. Das Betriebssystem lädt Images Speicher auf einem eigenen nicht verwalteten Heap. Und der einzige Weg, den wir an die Bilddaten bekommen können, ist, wenn wir für dieses spezifische Szenario einen Bisspunkt anfordern oder so . Sicher, wir können Zeiger verwenden. Aber was ist mit anderen Anwendungsfällen? Ist es nützlich, Datenstrukturen wie verknüpfte Listen und Binärbäume mit Zeigern zu implementieren ? Oder sollte einfach an einem Rennen bleiben, um es herauszufinden? Wir müssen die Leistung von Array-Operationen und Zeigeroperationen genau vergleichen , um zu sehen, ob es einen Unterschied zwischen ihnen gibt. Stellen wir uns also für einen Moment vor, dass mein gesamtes Betriebssystem tatsächlich in C scharf geschrieben ist . Ich führe diese Codes auf einem Makro Pro aus, und ich werde so tun, als ob das immer X-Betriebssystem tatsächlich in C scharf geschrieben ist . Wenn ich also ein Bild in den Speicher lade, die Bilddaten sofort als verwaltete von Tary verfügbar. Also habe ich ein zweites Programm geschrieben, das auf dieser Annahme basiert. Werfen Sie einen Blick auf dies Lassen Sie uns mit den ersten Messmethoden beginnen, Messung A Meine Bisse Array ist hier oben, Andi. Ich werde so tun, als wären dies die Bilddaten, die in dieses Array geladen wurden. Ich durchlaufe das Array bei Konvertierungen, jedes Pixel in Graustufen, mit der vertrauten Formel. Dieser Kult verwendet also die traditionelle Array-Indizierung, um jedes Pixel zu konvertieren. Jetzt ist hier der gleiche Code mit Zeigern umgeschrieben. Ich habe die festen Schlüsselwörter verwendet, um das Array im Speicher zu kneifen, und dann bekomme ich die Adresse von den ersten Elementen im Array mit dem 1% -Operator, und ich habe diese Adresse dem Biss-Zeiger in der Variablen P zugewiesen. Der Rest des Mantels ist genau das gleiche wie die Array-basierten Codes. Ich schaue über jedes Pixel mit dieser Variablen nach und benutzte Zeigerindizierung, um jedes nach seinem Wert zu lesen und zu schreiben . Und schließlich, hier ist ein weiterer Punkt basierter Methoden. Aber stattdessen aus, indem ich den Zehenzugriff, jedes Pixel, ich einfach den Zeiger selbst. Jede Schleife interational liest nur die roten grünen auf blauen Werten ein, ändert sie in Graustufen. Dann habe ich den Zeiger um drei Bisse erweitert, um auf das nächste Pixel zuzugreifen, so dass es immer noch eine Indizierung in dieser Methode gibt, aber nur um 01 oder zwei Bisse. Die wichtigsten Methoden hier unten ruft alle drei Methoden für unterschiedliche Bildgrößen auf. Ich misse die Leistung für simulierte Bilder von 512 bis zu 4096 Pixel Breite in Schritten 100 28. Nun, wie ich bereits erwähnt habe, verzerrt die Bildschirmaufnahme-Software meine Messungen, also bereitete ich die Ergebnisse im Voraus vor. Hier sind die Ergebnisse. Wie Sie sehen können, gibt es keinen großen Unterschied zwischen Measure A Measure B, mit einem brünstigen Array oder kauft Zeiger mit Indizierung, hat mehr oder weniger gleiche Leistung. Aber schauen Sie sich Measure C durchweg schneller an als die anderen beiden im Durchschnitt um 25%. Warum ist die Messung so viel schneller zu sehen? Um es herauszufinden, müssen wir einen Blick auf den Zwischencode werfen. Lassen Sie mich also sicherstellen, dass sich das Projekt in Debug-Modi befindet. Ich werde einen Bruchpunkt in den Hauptmethoden setzen, dann um das Programm auf Schalter zur Demontage Ansicht. Also los geht's. Ich werde mit der Maßnahme beginnen, eine Nachricht, die eine regelmäßige verwaltet von Tary verwendet. Der Code, der die Pixel ändert, ist hier. Sie können sehen. Es sind die vertrauten drei Ladeanweisungen und dann eine Store-Element-Anweisung, um die Array-Elemente zu ändern . Das Bild-Array befindet sich an Position Null. Die Indexvariable I befindet sich in Position zu, da sich der zu schreibende Wert in Position drei befindet. haben wir schon oft gesehen. Mittlerweile. Diese Codezeile ändert den nächsten Farbwert. Es ist fast der gleiche Mantel, aber die Variable I wird von einer mit diesen drei Ladeposition belastet, um Constant eins auf Add-Anweisungen in der dritten Codezeile zu laden , ist fast gleich. Aber jetzt erhöht die Variable I es um zwei. Und schließlich, hier drüben, die Variable I wird von drei mit diesen vier Laststandorttiefen Konstante ums auf Ladenstandortanweisungen belastet . Wie sieht dieser Mantel aus, wenn wir Zeiger verwenden? Lassen Sie mich nach unten zum Maß scrollen, Methoden sein und die entsprechenden Zeilen von Mantel finden Zuerst wird der Zeiger P mit dieser Ladeposition zur Anweisung geladen, dann die Variable. Ich bin mit diesem Hinweis geladen Location drei Anweisungen auf Rechnungen werden zusammen addiert, um die Speicheradresse zu erhalten , in die geschrieben werden soll. Schließlich wird der zu schreibende Wert mit diesem Ladeort zur Anweisung geladen. Der letzte Speicher in direkter Anweisung führt das eigentliche Recht auf Speicher aus. Sie sehen, dass diese Sequenz-Off-Anweisungen fast gleich sind, außer dass es eine zusätzliche Anweisung gibt, um die Speicheradresse zu berechnen und dass der Wert mit einem Speicher in direkter Anweisung anstelle einer Store-Element-Anweisung geschrieben wird . Die nächsten beiden Zeilen sind fast gleich, mit Ausnahme der zusätzlichen Add-Anweisung fügt entweder ein oder zwei zur Variablen I Lassen Sie uns nun einen Blick werfen. Das ist ein Maß C. Schreiben in Erinnerung ist nur diese drei Anweisungen lieben Ort. Drei. So laden Sie die Punkte sind Last Position fünf bis Tiefen. Der Graustufenwert Andi speichert indirekt, um in den Speicher zu schreiben. Die nächsten Zeilen haben diese beiden zusätzlichen Anweisungen laden Konstante eins und fügt zu Inkrement von eins ernannt und laden Konstanz auch, und fügen Sie hinzu, um den Zeiger um zwei zu erhöhen. Schließlich wird der Zeiger es um drei mit dieser Kälte hier erhöht, die identisch mit dem Kult ist, um die Variable I in den anderen beiden Methoden zu belasten. Also, warum ist das Erkältungen schneller? Nun, um zu beginnen, hat dieser Mantel weniger Zwischensprache Anweisungen als die anderen beiden Methoden Maßnahme benötigt werden . Fünf bei Operationen, um das Array zu ändern, wo dieser Kult nur Operationen hinzufügen und einen Bedarf messen muss. Drei Ladestandsanweisungen vor dem Aufruf von Store-Elementen, bei denen dieser Mantel nur Anweisungen benötigt . Auch der Punkt Traditionen sind viel kleiner im Maß sein. Wir müssen die Variable I auf den Punkt von P hinzufügen, um die Speicheradresse als Ende aus der Schleife zu erhalten , ich wächst zu sehr großen Werten bis zu 48 Millionen. Vergleichen Sie dies mit Methoden. Sehen Sie, wo sich der Zeiger befindet. Nur Inkrement ist um 12 oder drei Bisse. Kleine Zahlenzugänge sind viel effizienter als große Zahlenzugänge, da CP-Verwendung spezielle Anweisungen für die Inkrementierung von Wert um eins haben. Also, was können wir sagen? Abschließend? Wenn Zeiger Valets Wahl verwendet wird. Wir haben gesehen, dass eine Erhöhung des Indexpunkts von Operationen verwaltet gleichmäßig in Punktnetz durchgeführt. Aber für sehr große Indexwerte ist es viel effizienter, Terminplaner zu erhöhen und Daten in der Nähe der Punkt- oder Strömungsspeicheradresse zu lesen und zu schreiben . Und so ist dies der wichtigste Take Away für diesen Vortrag. Normalerweise sollten Sie keine Zeiger in C scharf verwenden, da die Leistungssteigerungen es einfach nicht wert sind . Indexzeiger und reguläre eindimensionale Arrays haben die gleiche Leistung. Um zu vermeiden, dass Sie unsichere Fell verwenden, sollten Sie ein Rennen wählen. Wie ist es in bestimmten Szenarien? Der Punkt des basierten Algorithmus kann einen Array-basierten Algorithmus um bis zu 25% für diese Szenarien übertreffen , Die folgenden Bedingungen müssen erfüllt werden. Zuerst lesen und schreiben Sie einen großen Block off Daten von mindestens 10 Megabyte Größe oder größer. Zweitens scannt der Algorithmus jeden Biss nacheinander oder springt in einer vorhersehbaren Weise durch die Daten und Schwerter während jeder Schleife. Iteration nur Daten, die sehr nahe an der aktuellen Zeigeradresse liegen, müssen gelesen oder geschrieben werden. Wenn Ihr Algorithmus diese Bedingungen erfüllt und Sie nicht daran denken, eine unsichere Assembly zu erzeugen , können Sie Zeiger verwenden und die zusätzlichen 25% der Leistung aufnehmen 23. Rückblick in den Kurs: Herzlichen Glückwunsch. Sie haben den gesamten Kurs absolviert. Sie sind jetzt ein zertifizierter C-Sharp-Code-Optimierer. Ich habe Ihnen über 25 verschiedene Arten von Anrufen Optimierung gezeigt. Wie Sie sie in Ihrer eigenen Beschichtungsanlage verwenden, welche spezifischen Leistungsverbesserungen sie Ihnen geben werden. Die Optimierung wurde über drei Abschnitte verteilt. Wir hatten die grundlegende Optimierung, die niedrig hängende Frucht, wo eine einfache Bearbeitung von Ihrem Mantel könnte zu einem massiven 100 Falten führen Leistungssteigerung. . Wir haben die zwischengeschalteten Organisationen, die komplexer waren, genannt die Rechte, die nur kleine bis mittlere Verbesserungen bieten. Wir mussten den harten Kern erweiterte Optimierung, wo wir mehrere exotische Features aus dem Net Framework verwendet, um die Leistung zu steigern. Diese Optimierung bietet Ihnen eine umfangreiche Toolbox mit Wissen und Ideen, die Sie verwenden können , wenn Sie Ihren eigenen Mantel schreiben oder wenn Sie in einem Entwicklungsteam zusammenarbeiten, vor allem, wenn Sie an geschäftskritischen Codes arbeiten, bei denen Spitzenleistung Entscheidend. Und wenn Sie eine interessante neue Performance-Technik Ihrer eigenen Polizei entdeckt haben, teilen Sie sie in der Kursdiskussion für ihn, für uns alle zu genießen. Ich hoffe, Sie genießen den Kurs auf gelernt einige nützliche neue Techniken, die Sie in Ihrer Software-Entwicklung Karriere anwenden können Jetzt gehen und bauen große Dinge