Share via


Leistungsoptimierung in Visual Basic .NET

Veröffentlicht: 08. Dez 2002 | Aktualisiert: 21. Jun 2004

Von Gordon Brown

In diesem Artikel werden einige wichtige Aspekte beschrieben, die Ihnen bei der Optimierung der Leistung Ihrer Anwendungen helfen können.

Auf dieser Seite

Einführung Einführung
Datentypen Datentypen
Deklarationen Deklarationen
Operationen Operationen
Prozeduren Prozeduren
Weitere Optimierungen Weitere Optimierungen
Schlussfolgerung Schlussfolgerung

Einführung

Eines der primären Ziele von Visual Basic .NET besteht darin, schneller und leistungsfähiger zu sein als frühere Versionen. Die Leistung hängt jedoch nach wie vor davon ab, wie Sie Ihr Programm entwerfen. Im übrigen Teil dieser Einführung werden die Bedingungen und Voraussetzungen überprüft, die diesen Optimierungsempfehlungen zugrunde liegen.

Intermediate Language
Visual Basic .NET und C# werden beide in Microsoft Intermediate Language (MSIL) kompiliert. Entsprechender Quellcode in den beiden Sprachen wird in denselben MSIL-Code kompiliert und resultiert in derselben Leistung für Ihre Anwendung. Somit sollte Leistung kein Kriterium für die Auswahl einer der beiden Sprachen sein. Weitere Informationen finden Sie unter Kompilieren in MSILHier verlassen Sie die Website von Microsoft Deutschland.

Ausführungshäufigkeit
Einige der Empfehlungen in diesem Artikel können einen geringfügigen Unterschied innerhalb einer Anweisung darstellen, der Leistungsgewinn in einer Schleife oder einer häufig aufgerufenen Prozedur kann jedoch wesentlich gesteigert werden. Daher stellen Codeblöcke, die sehr häufig ausgeführt werden, gute Kandidaten für eine Optimierung dar.

Engpässe
Der produktivste Lösungsansatz für eine Optimierung besteht darin, die Engpässe bzw. langsamen Stellen in Ihrer Anwendung zu ermitteln und auf deren Verbesserung hinzuarbeiten. Allgemeine Engpässe sind lange Schleifen und Datenbankzugriffe. Die Mikrooptimierung der einzelnen Ausdrücke und Aufrufe stellt keine effiziente Nutzung der Entwicklungsbemühungen dar.

Anwendungsabhängigkeit
Die Leistung hängt stark von den Eigenschaften der einzelnen Anwendungen ab. Es gibt keine Garantie. Die Empfehlungen in diesem Artikel dienen lediglich als Richtlinien. Es ist möglich, dass Sie einige Änderungen vornehmen müssen, um die jeweilige Anwendung zu optimieren.

Plattform
Visual Studio .NET ist für die empfohlene Systemhardwarekonfiguration optimiert - sowohl für die integrierte Entwicklungsumgebung (Integrated Development Environment, IDE) als auch für die Laufzeit. Wenn Ihnen weniger als der empfohlene Arbeitsspeicher zur Verfügung steht, kann die Leistung darunter leiden. Dies gilt insbesondere, wenn Sie große oder mehrere Anwendungen ausführen. Weitere Informationen zu den Systemvoraussetzungen finden Sie unter Suchen von InfodateienHier verlassen Sie die Website von Microsoft Deutschland.

Testbedingungen
Einige der Spezialtests für diesen Artikel wurden mit Visual Studio .NET 2002 unter Microsoft Windows 2000 Professional auf einem Pentium III mit 600 MHz und 256 MB RAM ausgeführt. Die Spezialtests umfassten enge Schleifen, die lediglich die zu vergleichenden Codeelemente ausführten. Mit anderen Worten: Die Schleifen enthielten nichts weiter als die Codeelemente. Die Unterschiede im Timing stellen somit die extremsten Fälle dar, und Sie sollten in einer normalen Anwendung nicht mit derartig großen Unterschieden rechnen.

Vorläufige Informationen
Dieser Artikel basiert auf vorläufigen Informationen. Die Empfehlungen können sich ändern, da die Erfahrung aktuellere und detailliertere Informationen hervorbringen wird. Einige der zugrunde liegenden Überlegungen können sich in künftigen Veröffentlichungen ebenfalls ändern.

 

Datentypen

Die Datentypen, die Sie für Ihre Variablen, Eigenschaften, Prozedurargumente und Prozedurrückgabewerte auswählen, können die Leistung Ihrer Anwendung beeinflussen.

Werttypen und Verweistypen

Werttypen speichern ihre Daten in einem eigenen zugeordneten Speicher. Da jede Werttypinstanz isoliert ist und nicht über mehr als eine Variable auf sie zugegriffen werden kann, können Werttypen auf dem Stapel gespeichert und verwaltet werden.

Verweistypen enthalten nur einen Zeiger, der auf den Speicherort verweist, an dem die Daten gespeichert sind. Da mehrere Variablen auf die Daten verweisen können, müssen Verweistypen auf dem Heap gespeichert und verwaltet werden.

Heapverwaltung beeinträchtigt die Leistung stärker als Stapelzuordnung. Bei Heapzuordnung, Objektzugriff und Garbage Collection (GC) kommt es zu einem Overhead. Das bedeutet, dass ein kleiner Werttyp die bessere Wahl darstellt als ein Verweistyp, wenn Sie nicht die Flexibilität eines Verweistyps benötigen. Mit zunehmender Größe verlieren Werttypen jedoch diesen Vorteil. Es dauert z.B. länger, eine Zuweisung mit einem Werttyp von 5 Byte vorzunehmen als mit einem Verweistyp.

Die Auswirkungen der Datentypoption auf die Leistung reichen von kaum merklich bis zu 30 Prozent zu Gunsten von kleinen Werttypen. Beachten Sie, dass sie auch von anderen Faktoren, wie Hardwareplattform, Systemladung und Datengröße, abhängt. Weitere Informationen finden Sie unter Werte- und VerweistypenHier verlassen Sie die Website von Microsoft Deutschland.

"Object"-Datentyp und späte Bindung

Eine als Object deklarierte Verweistypvariable kann auf Daten jeden Typs verweisen. Diese Flexibilität kann jedoch zu Lasten der Leistung gehen, da eine Object-Variable immer spät gebunden wird.

Eine Verweistypvariable wird früh gebunden, wenn sie für eine bestimmte Klasse deklariert wird (z.B. Form). Dadurch kann der Visual-Basic-Compiler bestimmte Optimierungen zur Kompilierzeit durchführen, z.B. Typenüberprüfung und Membersuche. Wenn Sie zur Laufzeit auf Member für eine früh gebundene Object-Variable zugreifen, hat der Compiler bereits einen großen Teil der Verwaltungsarbeit erledigt.

Eine Variable wird spät gebunden, wenn sie als Typ Object oder ohne expliziten Datentyp deklariert wird. Wenn Ihr Code auf Member für eine solche Variable zugreift, muss die Common Language Runtime die Typüberprüfung und Membersuche zur Laufzeit durchführen. Beachten Sie, dass eine Variable, die ohne expliziten Datentyp deklariert wurde, vom Typ Object ist, wenn Option Explicit auf Off gesetzt ist.

Die Leistung von früh gebundenen Objekten ist wesentlich besser als die von spät gebundenen Objekten. Außerdem erleichtern sie das Lesen und Verwalten Ihres Codes und reduzieren die Anzahl von Laufzeitfehlern. Das bedeutet, dass Sie Ihre Object-Variablen immer dann mit Hilfe eines bestimmten Klassentyps deklarieren sollten, wenn er Ihnen zur Entwicklungszeit bekannt ist. Weitere Informationen finden Sie unter Frühes und spätes BindenHier verlassen Sie die Website von Microsoft Deutschland.

Sie sollten die Verwendung des Typs Object vermeiden, sofern es nicht notwendig ist. Object-Variablen, die auf Daten eines Werttyps verweisen, hängen nicht nur von später Bindung ab, sondern belegen zusätzlichen Speicherplatz für den Zeiger und für eine weitere Kopie der Daten. Weitere Informationen finden Sie unter Object-DatentypHier verlassen Sie die Website von Microsoft Deutschland.

Datentypbreite

Die effizientesten Datentypen sind diejenigen, die die systemeigene Datenbreite der Laufzeitplattform verwenden. Auf aktuellen Plattformen beträgt die Datenbreite sowohl für den Computer als auch für das Betriebssystem 32 Bit.

Folglich ist Integer derzeit der effizienteste Datentyp in Visual Basic .NET. Long, Short und Byte stehen in Sachen Effizienz an zweiter, dritter und vierter Stelle. Sie können die Leistung von Short und Byte optimieren, indem Sie die Ganzzahlüberlauf-Überprüfung deaktivieren (z.B. durch Setzen der RemoveIntegerChecks-Eigenschaft). Dadurch entsteht jedoch das Risiko falscher Berechnungen aufgrund von nicht entdeckten Überläufen. Sie können diese Überprüfung während der Laufzeit nicht aktivieren und deaktivieren. Sie können den Wert lediglich für den nächsten Build der Anwendung setzen.

Wenn Sie Bruchzahlen benötigen, ist Double die beste Wahl, da die Gleitkommaprozessoren der derzeitigen Plattformen alle Operationen mit doppelter Präzision durchführen. Single und Decimal stehen in Sachen Effizienz hier an zweiter und dritter Stelle.

Darstellungseinheiten
In einigen Fällen kann Ihre Anwendung integrale Datentypen (Integer, Long, Short, Byte) anstelle von Bruchzahlen verwenden. Dies ist häufig der Fall, wenn Sie mehrere Einheiten zur Auswahl haben. Wenn Sie z.B. die Größe eines Bildes darstellen, können Sie Pixel statt Zoll oder Zentimetern verwenden. Da die Anzahl der Pixel in einem Bild immer eine ganze Zahl ist, können Sie sie in einer Integer-Variablen speichern. Wenn Sie Zoll verwenden, müssen Sie wahrscheinlich Bruchzahlen verarbeiten und benötigen somit eine Double-Variable, die nicht so effizient arbeitet. Die kleinste Darstellungseinheit kann gewöhnlich ganzzahlig sein, während es sich bei den größeren Einheiten normalerweise um Bruchzahl handelt. Wenn Sie viele Operationen mit diesen Einheiten durchführen, kann ein ganzzahliger Datentyp die Leistung verbessern.

Boxing und Unboxing

Unter Boxing ist die zusätzliche Verarbeitung zu verstehen, die von der Common Language Runtime übernommen werden muss, wenn Sie einen Werttyp als Verweistyp behandeln. Boxing ist z.B. erforderlich, wenn Sie eine Integer-Variable deklarieren und dann einer Object-Variablen zuweisen oder an eine Prozedur übergeben, die ein Object-Argument verwendet. In diesem Fall muss die Common Language Runtime die Variable "verpacken", um sie in den Typ Object umzuwandeln. Sie kopiert die Variable, bettet die Kopie in ein neu zugeordnetes Objekt ein und speichert ihre Typinformationen.

Wenn Sie die Boxingvariable anschließend einer als Werttyp deklarierten Variablen zuweisen, muss die Common Language Runtime sie "entpacken" (Unboxing), d.h. die Daten aus der Heapinstanz in die Werttypvariable kopieren. Darüber hinaus muss die Boxingvariable auf dem Heap verwaltet werden - unabhängig davon, ob sie entpackt wird oder nicht.

Das Boxing und Unboxing führt zu erheblichen Leistungsverlusten. Wenn Ihre Anwendung eine Werttypvariable häufig als Objekt behandelt, sollte sie sie möglichst zunächst als Verweistyp deklarieren. Eine Alternative wäre, die Variable einmal zu verpacken, die Boxingversion für die Verwendungsdauer beizubehalten und sie dann zu entpacken, wenn der Werttyp wieder benötigt wird.

Sie können versehentliches Boxing vermeiden, indem Sie Option Strict On setzen. Dadurch können Sie Stellen ausfindig machen, an denen Werttypen versehentlich verpackt werden, und werden zur Verwendung von expliziter Umwandlung gezwungen, die häufig effizienter ist als das Boxing. Beachten Sie jedoch, dass Sie das Boxing durch die Verwendung expliziter Umwandlung nicht umgehen können. CObj(<Werttyp>) und CType(<Werttyp>, Object) verpacken den Werttyp.

Arrays

Vermeiden Sie, einen größeren Rang zu verwenden als erforderlich. Je kleiner die Abmessungen eines Arrays sind, desto effizienter arbeitet es. Am deutlichsten wird der Unterschied zwischen ein- und zweidimensionalen Arrays, da die Common Language Runtime die Optimierung für eine Dimension durchführt.

Verzweigte Arrays (Arrays von Arrays) sind derzeit effizienter als rechteckige (mehrdimensionale) Arrays. Mit anderen Worten: A(9)(9) arbeitet effizienter als A(9,9). Der Grund dafür ist, dass verzweigte Arrays von der Optimierung für eindimensionale Arrays profitieren können. Der Unterschied kann mehr als 30 Prozent ausmachen.

Beachten Sie, dass verzweigte Arrays nicht der Common Language Specification (CLS) entsprechen. Das bedeutet, dass Sie verzweigte Arrays nicht über Klassen offen legen sollten, die CLS-kompatiblen Code verwenden sollen. Weitere Informationen finden Sie unter ArraysHier verlassen Sie die Website von Microsoft Deutschland.

ArrayList
Die ArrayList-Klasse im System.Collections-Namespace unterstützt ein dynamisches Array, das seine Größe je nach Bedarf ändert. Damit sie es verwenden können, müssen Sie eine Variable mit dem Datentyp ArrayList deklarieren, statt die Standard Array-Deklaration zu verwenden. Dann können Sie seine Methoden Add, AddRange, Insert, InsertRange, Remove und RemoveRange aufrufen, um Elemente hinzuzufügen und zu löschen.

Wenn sich die Größe Ihres Arrays häufig ändert und Sie die Werte vorhandener Elemente beibehalten müssen, bietet das ArrayList-Objekt u.U. mehr Leistung als die ReDim-Anweisung mit dem Preserve-Schlüsselwort. Der Nachteil von ArrayList ist, dass all seine Member vom Typ Object sind und daher spät gebunden werden. Ob der Vorteil gegenüber ReDim die Nachteile der späten Bindung aufwiegt, hängt von der jeweiligen Anwendung ab. Sie sollten bereit sein, beide Verfahren auszuprobieren und Leistungsvergleiche anzustellen. Weitere Informationen finden Sie unter ArrayList-KlasseHier verlassen Sie die Website von Microsoft Deutschland.

 

Deklarationen

Wie bereits im Abschnitt "Object-Datentyp und späte Bindung" erörtert wurde, bietet die frühe Bindung, die schneller als die späte Bindung ist, auch eine bessere Fehlerüberprüfung. Sie sollten Ihre Object-Variablen unter Verwendung des spezifischsten, am besten geeigneten Klassentyps deklarieren. Betrachten Sie z.B. die Teilvererbungshierarchie dieser Klassen im System.Windows.Forms-Namespace:
Object
Control
Button
Label
Form

Nehmen wir an, Sie setzen eine Object-Variable so ein, dass jedes Objekt, das Sie ihr zuweisen, ein Steuerelement (Control) ist und die meisten (jedoch nicht alle) von ihnen vom Typ Form sind. Obwohl Form ein spezifischerer Typ ist als Control, können Sie die Variable nicht als Typ System.Windows.Forms.Form deklarieren, da sie einige Objekte vom Typ Button oder Label verwenden könnte. Sie sollten die Variable als Typ System.Windows.Forms.Control deklarieren, da dies der spezifischste Typ ist, der jedes ihm zugewiesene Objekt akzeptieren kann. Weitere Informationen finden Sie unter System.Windows.Forms-NamespaceHier verlassen Sie die Website von Microsoft Deutschland.

Eigenschaften, Variablen und Konstanten
Variablen arbeiten schneller als Eigenschaften. Ein Variablenzugriff generiert einen einfachen Speicherabruf oder eine Speicherbelegung. Ein Eigenschaftenzugriff erfordert einen Aufruf der Get- oder Set-Methode für die jeweilige Eigenschaft, was manchmal zusätzliche Verarbeitung neben dem Abrufen oder Speichern des Werts erfordert. Beachten Sie, dass der Compiler WithEvents-Variablen als Eigenschaften implementiert, sodass sie die Leistungsvorteile anderer Variablen gegenüber Eigenschaften nicht teilen.

Konstanten arbeiten schneller als Variablen, da ihre Werte in den Code kompiliert werden. Ein Konstantenzugriff erfordert nicht einmal einen Speicherabruf, sofern es sich nicht um Konstanten vom Typ Date oder Decimal handelt.

Optionseinstellungen

Option Explicit On zwingt Sie dazu, alle Variablen zu deklarieren, wodurch Ihr Code leichter zu lesen und zu verwalten ist. Stellen Sie sicher, dass Sie in jeder Deklaration die As-Klausel verwenden, einschließlich der Prozedurargumente. Wenn Sie As nicht angeben, verwenden Ihre Variablen und Argumente den Datentyp Object, der gewöhnlich nicht der optimale Typ ist. Die Verwendung von As verbessert die Leistung, da dadurch die Typerstellung von der Laufzeit in die Kompilierzeit verlegt wird. Weitere Informationen finden Sie unter Option Explicit-AnweisungHier verlassen Sie die Website von Microsoft Deutschland.

Option Strict On verhindert implizites Einschränken, erfordert in jeder Deklaration die As-Klausel und unterbindet späte Bindung unabhängig von der Einstellung Option Explicit. Sie können nach wie vor Einschränkungstypen umwandeln, müssen jedoch explizite Umwandlungsschlüsselwörter verwenden, wie CInt und CType. Explizite Deklaration verbessert die Leistung, da sie Ihren Code vor einer versehentlichen späten Bindung schützt. Weitere Informationen finden Sie unter Option- Strict-AnweisungHier verlassen Sie die Website von Microsoft Deutschland.

Option Compare Binary legt fest, dass Zeichenfolgen basierend auf der binären Darstellung ihrer Zeichen verglichen und sortiert werden müssen, ohne entsprechende Zeichen zu berücksichtigen (z.B. Groß-/Kleinbuchstaben-Paare). Sie sollten den binären Vergleich verwenden, wenn immer die Logik Ihrer Anwendung dies zulässt. Er verbessert die Leistung, da der Code sich nicht mit der Nichtbeachtung von Groß-/Kleinschreibung oder mit Gruppen von Zeichen befassen muss, die in einer gegebenen Kultur als alphabetisch gleichwertig betrachtet werden.

Objektauflistungen und Objektarrays

Wenn Sie eine Gruppe von verwandten Objekten haben, die ähnlich verarbeitet werden, können Sie sie in einem Array von Objekten platzieren oder eine Auflistung erstellen, in der die Objekte als Member fungieren. Die folgenden Überlegungen können die Entscheidung zwischen diesen Schemata erleichtern:

  • Die Common Language Runtime kann den Code für ein Array optimieren, während jeder Zugriff auf eine Auflistung einen oder mehrere Methodenaufrufe erfordert. Daher sind Arrays normalerweise vorzuziehen, wenn sie alle durchzuführenden Operationen unterstützen.

  • Bei indiziertem Zugriff sind Arrays grundsätzlich nicht langsamer und gewöhnlich schneller als Auflistungen.

  • Für mit Schlüsseln versehenen Zugriff sollten Sie eine Auflistung verwenden. Da Arrays den Zugriff mit einem Schlüsselfeld nicht unterstützen, müssten Sie Code schreiben, um die Elemente des Arrays nach dem Schlüssel zu durchsuchen.

  • Für Einfügungen und Löschungen sind gewöhnlich Auflistungen vorzuziehen. Arrays bieten keine direkte Unterstützung für das Hinzufügen und Entfernen von Elementen. Wenn Sie am Ende eines Arrays Elemente einfügen oder löschen, müssen Sie die ReDim-Anweisung verwenden, die die Leistung reduziert. Zum Einfügen und Löschen an anderer Stelle müssen Sie ein ArrayList-Objekt statt eines Standardarrays verwenden. Im Gegensatz dazu stellen Einfügungen und Löschungen in einer Auflistung einfache Operationen dar, die dementsprechend schnell sind und nicht von der Position der betreffenden Elemente abhängen.

Datenträgerdatei-E/A

Visual Basic .NET bietet drei wesentliche Möglichkeiten für den Zugriff auf Datenträgerdateien:

  • Herkömmliche Visual-Basic-Laufzeitfunktionen, wie FileOpen und WriteLine

  • Das FileSystemObject-Objektmodell (FSO) von Microsoft Scripting Runtime

  • Das .NET-Framework-Objektmodell im System.IO-Namespace

Die herkömmlichen Laufzeitdateifunktionen werden für die Kompatibilität mit früheren Versionen von Visual Basic bereitgestellt. FSO wird für die Kompatibilität mit Skriptsprachen und für Anwendungen, die diese Funktionalität erfordern, bereitgestellt. Jedes dieser Modelle ist als Gruppe von Wrapperobjekten implementiert, die Member von Klassen im System.IO-Namespace aufrufen. Daher sind sie nicht so effizient wie die direkte Verwendung von System.IO. Außerdem erfordert FSO, dass Ihre Anwendung COM-Interop implementiert, wodurch ein Marshallingoverhead entsteht.

Sie können die Leistung verbessern, indem Sie u.a. die folgenden System.IO-Klassen verwenden:

  • Path, Directory und File für die Verarbeitung auf Laufwerk- oder Ordnerebene

  • FileStream für allgemeines Lesen und Schreiben

  • BinaryReader und BinaryWriter für Dateien mit binären oder unbekannten Daten

  • StreamReader und StreamWriter für Textdateien

  • BufferedStream zur Bereitstellung eines Puffers für einen E/A-Stream

FileStream liefert im Allgemeinen die effizienteste Datenträgerleistung. Es führt eigene Puffer und Datenträgeroperationen aus, ohne mehr als die Windows-E/A-Prozeduren zu wrappen. Wenn die Datenträger-E/A jedoch keinen Engpass in Ihrer Anwendung darstellt, ist u.U. eine der anderen Klassen praktischer. Sie ziehen z.B. möglicherweise StreamReader und StreamWriter vor, wenn Sie nur mit Text arbeiten und die Datenträgerleistung nicht entscheidend ist.

Puffer
Deklarieren Sie Ihre Puffer mit einer vernünftigen Größe. Dies gilt insbesondere, wenn Sie BufferedStream oder FileStream verwenden. Die Idealgröße ist gewöhnlich ein Mehrfaches von 4 KB, obwohl dieser Wert je nach Anwendung variieren kann. Puffer, die kleiner als 4 KB sind, können die Leistung beeinträchtigen, indem sie zu viele E/A-Operationen für eine bestimmte Datenmenge in Gang setzen. Zu große Puffer können mehr Speicher belegen als notwendig, um eine bestimmte Leistungsverbesserung zu erzielen. Je nach Hardware kann die annehmbare obere Grenze zwischen 8 und 64 KB variieren.

Asynchrone E/A
Wenn Ihre Anwendung datenträgergebunden ist, können Sie die Datenträgerlatenz nutzen, um andere Aufgaben durchzuführen, während Sie auf die Beendigung der E/A-Übertragung warten. Verwenden Sie dazu die asynchrone E/A, die bei der FileStream-Klasse zur Verfügung steht. Dieses Verfahren kann zwar mehr Quellcode erfordern, optimiert jedoch die Laufzeitleistung, da ein Teil des Codes ausgeführt wird, während der Lese- oder Schreibvorgang läuft. Wenn Sie große Datenmengen übertragen oder eine erhebliche Datenträgerlatenz vorhanden ist, kann die Optimierung recht umfangreich sein.

 

Operationen

Ganzzahlarithmetik ist wesentlich schneller als Gleitkomma- oder Dezimalarithmetik. In Berechnungen, bei denen Sie weder Dezimalstellen noch Teilwerte benötigen, deklarieren Sie alle Variablen und Konstanten als ganzzahlige Datentypen, vorzugsweise Integer. Denken Sie jedoch daran, dass die Umwandlung in und aus Gleitkommas die Leistung beeinträchtigt.

Operatoren

Verwenden Sie beim Trennen von integralen Werten die Ganzzahltrennung (\-Operator), wenn Sie lediglich den Quotienten und nicht den Rest benötigen. Der \-Operator kann zehn Mal so schnell arbeiten wie der /-Operator.

Zuweisungsoperatoren
Zuweisungsoperatoren wie += sind präziser als die Operatoren, aus denen sie bestehen (separates = und +), und können das Lesen Ihres Codes erleichtern. Wenn Sie Operationen für einen Ausdruck statt für eine einfache Variable (z.B. ein Arrayelement) durchführen, können Sie mit Zuweisungsoperatoren eine bedeutende Leistungsverbesserung erzielen. Der Grund dafür ist, dass der Ausdruck (z.B. MyArray(SubscriptFunction(Arg1, Arg2))) nur einmal bewertet werden muss.

Verkettungen
Zum Verketten von Zeichenfolgen sollten Sie den Verkettungsoperator (&) statt des Plusoperators (+) verwenden. Diese beiden Operatoren entsprechen sich nur, wenn beide Operanden vom Typ String sind. Wenn dies nicht der Fall ist, wird der +-Operator spät gebunden und muss Typenüberprüfungen und Umwandlungen durchführen. Der &-Operator wurde eigens für Zeichenfolgen entworfen.

Boolesche Tests
Wenn Sie eine Boolean-Variable in einer If-Anweisung testen, lässt sich diese leichter lesen, wenn Sie nur die Variable in dem Test angeben, statt sie anhand des =-Operators mit TRUE zu vergleichen. Obwohl es keine bedeutenden Leistungsunterschiede gibt, stellt der zweite Test im folgenden Beispiel das bessere Programmierverfahren dar:

If BoolVariable = True Then   ' Unnötige Angabe von TRUE. 
' ...
End If
If BoolVariable Then   ' Kompakter.
' ...
End If

Umgehungen
Wenn möglich, sollten Sie die Boolean-Umgehungsoperatoren AndAlso und OrElse verwenden. Diese können Zeit sparen, indem sie die Bewertung eines Ausdrucks in Abhängigkeit von dem Ergebnis eines anderen umgehen. Wenn im Fall von AndAlso das Ergebnis des Ausdrucks auf der linken Seite FALSE ist, steht das Endergebnis bereits fest, und der Ausdruck auf der rechten Seite wird nicht bewertet. In ähnlicher Weise umgeht OrElse den Ausdruck auf der rechten Seite, wenn der Ausdruck auf der linken Seite mit TRUE bewertet wird. Beachten Sie auch, dass die Case-Anweisung eine Liste von mehreren Ausdrücken und Bereichen umgehen kann, wenn sie vor dem Ende der Liste auf einen übereinstimmenden Wert stößt.

Memberzugriff
Einige Memberzugriffe (.-Operator) rufen eine Methode oder Eigenschaft auf, die ein Objekt zurückgibt. In der System.Windows.Forms.Form-Klasse gibt z.B. die Controls-Eigenschaft ein ControlCollection-Objekt zurück. Ein solcher Zugriff umfasst Objekterstellung, Heapzuordnung und -verwaltung sowie Garbage Collection (GC). Wenn Sie diese Art von Memberzugriff in einer Schleife durchführen, erstellen Sie jedes Mal ein neues Objekt, wodurch sich die Leistung verlangsamt. Wenn Sie in jeder Schleifeniteration dasselbe Objekt verarbeiten möchten, könnte dies auch ein Logikfehler sein, da jeder Zugriff dieses Typs ein anderes Objekt erstellt.

Vermeiden von Neuqualifizierung
Wenn Sie viele Verweise auf Member eines qualifizierten Elements (z.B. MyForm.Controls.Item(Subscript)) erstellen, können Sie die Leistung durch die Verwendung der With ... End With-Konstruktion verbessern:

With MyForm.Controls.Item(Subscript)   ' Qualifikation einmal bewerten.
   .Name = "Control number " & CStr(Subscript)
   .Text = .Name
   ' Auf andere Member von MyForm.Controls.Item(Subscript) zugreifen
   .Refresh()
End With

Der oben stehende Code bewertet MyForm.Controls.Item(Subscript) nur einmal. Er kann mehr als doppelt so schnell ausgeführt werden wie eine Neuqualifizierung der einzelnen Memberzugriffe. Wenn das Element jedoch nicht qualifiziert ist (z.B. AnswerForm oder Me), findet bei Verwendung von With ... End With keine Leistungsverbesserung statt. Weitere Informationen finden Sie unter With...End With-AnweisungenHier verlassen Sie die Website von Microsoft Deutschland.

Typumwandlungen
Zwei Klassentypen haben eine Vererbungsbeziehung, wenn sich die eine aus der anderen ableitet. Wenn Sie Objekte von jedem dieser Typen besitzen und eines davon in den Typ eines anderen umwandeln müssen, können Sie statt des CType-Schlüsselworts das DirectCast-Schlüsselwort verwenden. Die Leistung von DirectCast kann erheblich besser ausfallen, da es keine Laufzeithilfsfunktionen verwendet. Beachten Sie, dass DirectCast einen InvalidCastException-Fehler auslöst, wenn keine Vererbungsbeziehung zwischen den beiden Typen besteht. Weitere Informationen finden Sie unter DirectCastHier verlassen Sie die Website von Microsoft Deutschland.

Zwischenspeichern von Eigenschaften

Wenn Sie wiederholt auf eine Eigenschaft zugreifen (z.B. in einer Schleife, die sehr häufig ausgeführt wird), können Sie die Leistung verbessern, indem Sie den Eigenschaftswert zwischenspeichern. Dazu müssen Sie den Eigenschaftswert einer Variablen zuweisen, bevor er in die Schleife gelangt, und die Variable dann während der Schleife verwenden. Sofern nötig, können Sie den Variablenwert wieder der Eigenschaft zuweisen, nachdem die Schleife ausgeführt wurde.

Wenn der Zugriff auf wiederholt verwendete Eigenschaften einen wesentlichen Teil des Codes in der Schleife darstellt, kann das Zwischenspeichern der Eigenschaften die Ausführung der Schleife auf das Dreifache beschleunigen.

Eine Eigenschaft lässt sich jedoch möglicherweise nicht speichern, wenn ihre Get- und Set-Methoden neben dem Abrufen oder Speichern des Werts zusätzliche Verarbeitungsvorgänge durchführen.

Ausnahmebehandlung

Die herkömmliche Visual-Basic-Fehlerbehandlung verwendet die Anweisungen On Error GoTo und On Error Resume Next. Diese sind nicht immer einfach zu entwerfen, und der resultierende Quellcode ist häufig kompliziert und schwer zu lesen und zu verwalten.

Visual Basic .NET bietet eine strukturierte Ausnahmebehandlung mit den Try ... Catch ... Finally-Anweisungen. Sie basiert auf einer Steuerungsstruktur, die flexibel und leicht zu lesen ist. Strukturierte Ausnahmebehandlung kann einen gegebenen Codeblock auf mehrere verschiedene Ausnahmen überprüfen und jede von ihnen auf unterschiedliche Weise behandeln.

Beide Verfahren sind mit einem gewissen Leistungsoverhead verbunden. Die Verwendung der On Error Resume Next-Anweisung zwingt den Compiler, für jede Quellcodeanweisung in dem Block, der der On Error Resume Next-Anweisung folgt, weitere Intermediate Language (IL) zu generieren. Ein Catch-Block ändert den Status des Err-Objekts am Ein- und Ausgang. Vorläufige Tests haben ergeben, dass die Leistung bei beiden Verfahren ungefähr gleich ist, wenn der Block weniger als 20 Quellanweisungen enthält. Blöcke mit mehreren hundert Anweisungen sollten mit Try und Catch jedoch bessere Leistungen erzielen, da On Error Resume Next mehr IL für ansonsten identischen Quellcode generiert.

Wenn Sie keinen triftigen Grund für die Verwendung von On Error-Anweisungen haben, sollten Sie strukturierte Ausnahmebehandlung verwenden. Weitere Informationen finden Sie unter Strukturierte AusnahmebehandlungHier verlassen Sie die Website von Microsoft Deutschland.

Auslösen von Ausnahmen
Obwohl strukturierte Ausnahmebehandlung nützlich ist, sollten Sie sie ausschließlich für Ausnahmen verwenden. Eine Ausnahme ist nicht unbedingt ein Fehler, doch sollte es sich dabei um ein unregelmäßig auftretendes Ereignis handeln, mit dem im Normalbetrieb nicht gerechnet wird. Das Auslösen von Ausnahmen erfordert mehr Verarbeitungszeit als das Testen und Verzweigen (z.B. mit Hilfe einer Select-Konstruktion oder einer While-Schleife). Ausnahmen erschweren außerdem das Lesen des Codes, wenn sie für die normale Flusssteuerung verwendet werden. Sie sollten sie nicht zum Verzweigen oder Zurückgeben von Werten verwenden.

Auffangen von Ausnahmen
Try ... Catch ... Finally-Konstruktionen verursachen einen sehr geringen Leistungsoverhead, sofern keine Ausnahme ausgelöst wird. Mit anderen Worten: Das Erstellen eines Ausnahmehandlers hat keine beeinträchtigenden Auswirkungen auf die Leistung, und Sie sollten nicht zögern, strukturierte Ausnahmebehandlung zu verwenden, wenn Sie nur selten mit dem Auftreten der Ausnahme rechnen.

Zeichenfolgen

Instanzen der String-Klasse sind unveränderlich. Folglich lassen Sie jedes Mal, wenn Sie eine String-Variable ändern, die Zuordnung des vorhandenen String-Objekts unangetastet und erstellen ein neues. Dies kann einen hohen Speicher- und Leistungsoverhead verursachen, wenn Sie dieselbe String-Variable mehrmals manipulieren. Die gängigste Art der Manipulation ist die Verkettung, doch String-Methoden, wie Insert und PadRight, generieren ebenfalls neue Instanzen.

StringBuilder
Die StringBuilder-Klasse im System.Text-Namespace unterstützt eine veränderliche Zeichenfolge, die auch nach der Änderung dieselbe Instanz beibehält. Wenn Sie sie verwenden möchten, müssen Sie eine Zeichenfolgenvariable mit dem Datentyp StringBuilder statt String deklarieren. Dann können Sie ihre Methoden Append, Insert, Remove und Replace aufrufen, um die Zeichenfolge zu manipulieren.

Wenn Sie eine größere Anzahl von Verkettungen oder andere Änderungen vornehmen, kann die StringBuilder-Klasse bis zu drei Mal so schnell arbeiten wie der Datentyp String. Bei Bedarf können Sie die endgültigen Zeichenfolgendaten mit Hilfe der ToString-Methode in ein String-Objekt kopieren, sobald die Manipulationen abgeschlossen sind.

Wenn Sie voraussichtlich jedoch nicht besonders häufig dieselbe Instanz manipulieren, ist String die bessere Wahl. Der Grund dafür ist, dass StringBuilder einen einmaligen Overhead mit sich bringt, String jedoch nicht. Zur Erstellungszeit benötigt der StringBuilder-Konstruktor mehr Zeit als der String-Konstruktor. Abschließend müssen Sie in den meisten Fällen ToString aufrufen. Weitere Informationen finden Sie unter StringBuilder-KlasseHier verlassen Sie die Website von Microsoft Deutschland.

Verkettung
Manchmal können Sie alle Zeichenfolgenänderungen in einer einzelnen Anweisung kombinieren, z.B.:

MyString = PrefixString & ": " & MyString & " -- " & SuffixString

Eine Anweisung wie die im oben stehenden Beispiel erstellt nur einmal eine neue Zeichenfolge, und es gibt keine späte Bindung, da sie den &-Operator verwendet.

Wenn Sie Zeichenfolgenkonstanten in einer Anweisung verketten, kombiniert Visual Basic diese zur Kompilierzeit. Dadurch wird die endgültige resultierende Zeichenfolge in Intermediate Language (IL) generiert, wodurch sich die Leistung zur Laufzeit verbessert. Beachten Sie, dass das Ergebnis der ChrW-Funktion als Konstante verwendet werden kann, wenn ihr Argument eine Konstante ist.

Zeichenfolgenfunktionen
Die Format-Funktion übernimmt einen großen Teil der Überprüfung, Typumwandlung und anderer Verarbeitungsvorgänge, einschließlich der Formatierung in Übereinstimmung mit der aktuellen Kultur. Wenn Sie keine dieser speziellen Funktionen benötigen, verwenden Sie stattdessen die entsprechende ToString-Methode. Die ToString-Methoden sind schneller als das CStr-Umwandlungsschlüsselwort, da CStr vor dem Aufrufen von ToString eine zusätzliche Analyse durchführt.

Die Funktionen Asc und Chr arbeiten mit SBCS- und DBCS-Codepunkten (Single-Byte Character Set und Double-Byte Character Set). Sie müssen die Codeseite für den aktuellen Thread konsultieren und die Zeichen dann in und aus Unicode übersetzen. Die Funktionen AscW und ChrW sind effizienter, da sie ausschließlich in Unicode arbeiten und unabhängig von der Kultur und den Codeseiteneinstellungen für den aktuellen Thread sind.

 

Prozeduren

Es gibt einen Nachteil beim Aufrufen einer Prozedur innerhalb einer Schleife und dem Platzieren des Prozedurtextes in der Schleife. Wenn Sie den Prozedurcode in die Schleife integrieren, vermeiden Sie den Overhead des Aufrufmechanismus. Andere Stellen in Ihrer Anwendung können jedoch nicht auf diesen Code zugreifen. Wenn Sie den Code an anderer Stelle duplizieren, erschweren Sie außerdem die Wartung und gehen das Risiko von Fehlern bei der Aktualisierungssynchronisierung ein.

Wenn Sie die Prozedur außerhalb der Schleife definieren, lässt sich der Schleifencode leichter lesen. Außerdem ist die Prozedur von anderen Stellen in Ihrer Anwendung aus zugänglich. Bei großen Prozeduren ist der Aufruf-Overhead nicht von Bedeutung. Er kann jedoch wesentlich werden, wenn die Prozedur nur eine kleine Aufgabe erledigt, z.B. den Zugriff auf ein Member eines Klassenobjekts. In einem solchen Fall erzielen Sie eine bessere Leistung, indem Sie direkt in der Schleife auf das Member zugreifen.

Prozedurgröße
Prozeduren hängen von der JIT-Kompilierung (Just-In-Time) ab. Eine Prozedur kann erst dann kompiliert werden, wenn sie zum ersten Mal aufgerufen wird. Der JIT-Compiler versucht beim Kompilieren einer Prozedur eine Reihe von Optimierungen durchzuführen, wie das Generieren von internem Code für kleine Prozeduraufrufe. Sehr große Prozeduren können von solchen Optimierungen nicht profitieren. Als Richtlinie gilt, dass eine Prozedur, die mehr als ungefähr 1000 Codezeilen enthält, wahrscheinlich nicht von der JIT-Optimierung profitieren kann.

Aufrufen und Zurückgeben
In früheren Versionen von Visual Basic war es in einigen Fällen schneller, eine Prozedur über ihr eigenes Modul bzw. Projekt aufzurufen als über ein anderes. In Visual Basic .NET macht beides keinen Unterschied. Daher stellt der Speicherort einer Prozedur im Verhältnis zum aufrufenden Code kein Leistungskriterium dar.

Verwenden Sie die Return-Anweisung, wenn immer die Logik dies zulässt. Weitere Informationen finden Sie unter Return-AnweisungHier verlassen Sie die Website von Microsoft Deutschland. Der Compiler kann den Code besser optimieren, als wenn Sie Exit Function, Exit Property oder Exit Sub verwenden oder von den Anweisungen End Function, End Get, End Set oder End Sub eine Rückgabe generieren lassen.

Virtuelle Aufrufe
Ein virtueller Aufruf ist ein Aufruf einer überschreibbaren Prozedur. Beachten Sie, dass dies lediglich davon abhängt, ob die Prozedur mit dem Overridable-Schlüsselwort deklariert wurde und nicht davon, ob Überschreibungen definiert wurden. Wenn Sie einen virtuellen Aufruf durchführen, muss die Common Language Runtime den Laufzeittyp des Objekts überprüfen, um zu bestimmen, welche Überschreibung aufgerufen werden soll. Im Gegensatz dazu kann ein nicht virtueller Aufruf alle erforderlichen Informationen über den Kompilierzeittyp abrufen.

Vom Leistungsstandpunkt aus gesehen dauern virtuelle Aufrufe ungefähr doppelt so lange wie nicht virtuelle Aufrufe. Der Unterschied macht sich insbesondere bei einem Werttyp bemerkbar, z.B. wenn Sie eine Prozedur für eine Struktur aufrufen. (Eine Struktur kann keine Overridable-Member deklarieren, erbt jedoch Equals, GetHashCode und ToString von Object und kann Overridable-Member von Schnittstellen implementieren.) Sie sollten Overridable-Prozeduren nur definieren, wenn es einen klaren architektonischen Vorteil gibt, und die Aufrufe so weit wie möglich auf NotOverridable-Prozeduren beschränken.

Prozedurargumente

Wenn Sie ein Argument mit Hilfe des ByRef-Schlüsselworts an eine Prozedur übergeben, kopiert Visual Basic nur einen Zeiger, der auf die zugrunde liegende Variable verweist - unabhängig davon, ob diese Variable ein Werttyp oder ein Verweistyp ist. Wenn Sie ByVal an ein Argument übergeben, wird der Inhalt der zugrunde liegenden Variablen kopiert. Bei einem Verweistyp besteht dieser Inhalt lediglich aus einem Zeiger, der auf das Objekt selbst verweist. Bei einem Werttyp besteht er aus allen Daten der Variablen.

Der Leistungsunterschied zwischen ByRef und ByVal ist gewöhnlich unerheblich, insbesondere bei Verweistypen. Bei Werttypen hängt der Unterschied von der Datenbreite des Typs ab. Die meisten Werttypen haben eine Datenbreite, die ungefähr der Größe eines Zeigers entspricht, und in diesem Fall ist die Leistung gleich. Ein großer Werttyp, z.B. eine lange Struktur, kann beim Übergeben von ByRef effizienter sein, um das Kopieren aller Daten zu vermeiden. Beim Übergeben eines Werttyps optimaler Größe (Integer oder Double) ist andererseits u.U. ByVal vorzuziehen, da der Compiler häufig den aufrufenden Code optimieren kann, z.B. durch Speichern des Arguments in einem Register.

Da der Leistungsunterschied zwischen ByVal und ByRef gewöhnlich nicht so erheblich ist, können Sie bei der Auswahl eines der beiden Übergabemechanismen andere Kriterien berücksichtigen. Das Übergeben von ByRef an ein Argument hat den Vorteil, dass die Prozedur einen Wert an den aufrufenden Code zurückgeben kann, indem sie den Inhalt der Variable ändert, die Sie an das Argument übergeben. Das Übergeben von ByVal an ein Argument hat den Vorteil, dass es Variablen davor schützt, von der Prozedur geändert zu werden.

Wenn es keinen triftigen Grund gibt, ByRef an ein Argument zu übergeben, sollten Sie ByVal übergeben. Weitere Informationen finden Sie unter Argumentübergabe ByVal und ByRefHier verlassen Sie die Website von Microsoft Deutschland.

Schleifen

Minimieren Sie die Anzahl der Schleifen in einem Try-Block und die Anzahl der Try-Blöcke in einer Schleife. Eine lange Schleife könnte den Overhead von strukturierten Ausnahmebehandlungen vergrößern.

Es gibt Spekulationen darüber, ob For-, Do- oder While-Schleifen am effizientesten sind. Vorläufige Tests haben keine bedeutenden bzw. konsistenten Unterschiede zwischen diesen dreien ergeben. Daher ist die Leistung nicht ausschlaggebend für die Auswahl eines dieser Schleifentypen.

Auflistungen

Wenn Sie eine Auflistung durchlaufen, können Sie entweder eine For-Schleife oder eine For Each-Schleife verwenden. Wenn Sie damit rechnen, dass während des Durchlaufens Auflistungselemente hinzugefügt, gelöscht oder neu angeordnet werden, erzielt eine For-Schleife u.U. zuverlässigere Ergebnisse. Außerdem ermöglicht sie es, die Durchlaufreihenfolge zu bestimmen. Darüber hinaus löst eine Auflistung, die sich aus der CollectionBase-Klasse im System.Collections-Namespace ableitet, eine Ausnahme aus, wenn Sie versuchen, ihre Member während einer For Each-Schleife zu ändern.

Eine For Each-Schleife gibt dem Enumeratorobjekt, das von der IEnumerable.GetEnumerator-Methode zurückgegeben wird, Kontrolle über die Auflistung. Das bedeutet, dass Sie die Durchlaufreihenfolge nicht unbedingt vorherbestimmen können. For Each ist sinnvoll, wenn Sie nicht mit der Item-Eigenschaft auf die Member einer Auflistung zugreifen können.

Der Leistungsunterschied zwischen der For- und der For Each-Schleife scheint nicht erheblich zu sein.

Hinzufügen von Membern
Wenn Sie die Add-Methode einer Auflistung aufrufen, vermeiden Sie die Verwendung der Argumente Before und After. Wenn Sie eine Position in der Auflistung mit Before oder After angeben, zwingen Sie die Auflistung, ein anderes Member zu suchen, bevor sie Ihr neues Member hinzufügen kann.

Wenn Sie eine Auflistung verwenden, die eine AddRange-Methode besitzt, sollten Sie sie der Add-Methode vorziehen. AddRange fügt in einem Aufruf eine ganze Liste oder Auflistung hinzu. Mehrere Auflistungsklassen legen die AddRange-Methode offen, z.B. die ArrayList-Klasse im System.Collections-Namespace und die ComboBox.ObjectCollection-Klasse im System.Windows.Forms-Namespace.

Threading

Wenn Ihre Anwendung einen großen Teil ihrer Zeit darauf verwendet, auf die Beendigung einiger Operationen zu warten, sollten Sie die Verwendung der asynchronen Verarbeitung in Erwägung ziehen und die Methoden der Thread-Klasse im System.Threading-Namespace nutzen. Dies kann beim Warten auf Benutzerantworten sowie beim Lesen und Schreiben in persistenten Medien sinnvoll sein. Asynchrone Verarbeitung und Threading umfassen zwar zusätzliche Codierung, können jedoch hinsichtlich der Leistung einen großen Unterschied machen.

Denken Sie jedoch daran, dass Threading einen Overhead mit sich bringt und mit Vorsicht zu verwenden ist. Ein Thread mit einer kurzen Lebensdauer ist von sich aus ineffizient, und das Umschalten zwischen Kontexten nimmt einen bedeutenden Teil der Ausführungszeit in Anspruch. Sie sollten die Mindestanzahl von langfristigen Threads verwenden und so selten wie möglich zwischen ihnen umschalten.

Mehrfachpufferung
Wenn Ihre Anwendung übermäßige Schreib- und Lesevorgänge ausführt und Sie asynchrone E/A verwenden, könnte Mehrfachpufferung von Vorteil sein. Bei der Mehrfachpufferung ordnen Sie einer zu lesenden oder schreibenden Datei zwei oder mehr Puffer zu. Während Sie darauf warten, dass die E/A in einem Puffer beendet wird, können Sie die Daten in einem anderen Puffer verarbeiten. Mehrfachpufferung wird auch als Swingpufferung bezeichnet. Bei Verwendung von zwei Puffern wird sie im Allgemeinen Doppelpufferung genannt.

Prozessübergreifende Aufrufe

Aufgrund des Overheads für das Marshalling sollten Sie prozessübergreifende Aufrufe, Remoteaufrufe und anwendungsdomänenübergreifende Aufrufe minimieren. Dies gilt insbesondere für COM-Interop-übergreifende Aufrufe, d.h. Aufrufe zwischen verwaltetem und nicht verwaltetem Code (COM). Wenn Sie solche Aufrufe durchführen müssen, versuchen Sie, sie in ein paar kompakten Aufrufen zusammenzufassen, statt sie in viele kleine Einzelaufrufe zu unterteilen. Ein kompakter Aufruf führt mehrere Aufgaben aus, z.B. das Initialisieren aller Felder für ein Objekt. Ein Einzelaufruf führt vor dem Zurückgeben nur eine kurze Aufgabe durch. Weitere Informationen finden Sie unter Programmieren mit Anwendungsdomänen und AssembliesHier verlassen Sie die Website von Microsoft Deutschland.

Blitfähige Datentypen
Blitfähige Typen, die sowohl im verwalteten als auch im nicht verwalteten Speicher auf dieselbe Weise dargestellt werden, können ohne Umwandlung über die Grenze zwischen verwaltetem und nicht verwaltetem Code hinaus kopiert werden. Wenn Ihre Anwendung COM-Interop-Aufrufe durchführt, versuchen Sie, nur blitfähige Datentypen für die Argumente zu verwenden. Die blitfähigen Typen in Visual Basic .NET sind Byte, Short, Integer, Long, Single und Double. Die Common Language Runtime-Typen System.SByte, System.UInt16, System.UInt32 und System.UInt64 sind ebenfalls blitfähige Typen. Weitere Informationen finden Sie unter Blitfähige und nicht blitfähige TypenHier verlassen Sie die Website von Microsoft Deutschland.

Einige zusammengesetzte Datentypen können ebenfalls blitfähige Typen sein. Eine Struktur, die ausschließlich aus blitfähigen Membern besteht, besitzt selbst auch die Eigenschaften eines blitfähigen Typs. Eine Klasse entspricht nicht automatisch einem blitfähigen Typ, auch wenn sie nur blitfähige Member enthält. Sie können die Marshallingleistung jedoch immer noch verbessern, indem Sie nur blitfähige Member verwenden. Außerdem können Sie das ExplicitLayout-Member der TypeAttributes-Enumeration im System.Reflection-Namespace einrichten. Dadurch werden die Klassenmember zwangsläufig zu den angegebenen Offsets ohne Neuausrichtung durch die Common Language Runtime gemarshallt.

Marshalling
Marshalling macht einen bedeutenden Teil der prozessübergreifenden Aufrufe aus. Eine Reduzierung des Marshalling kann Leistungsverbesserungen herbeiführen. Blitfähige Datentypen und explizites Memberlayout gehören zu den effektivsten Möglichkeiten, den Marshallingoverhead zu minimieren. Die Leistung wird erhöht, wenn möglichst Strukturen statt Klassen verwendet werden.

Verwalteter Code kann ebenfalls die Plattformaufruffunktionalität verwenden, um nicht verwaltete Funktionen aufzurufen, die in DLLs (Dynamic-Link Libraries) implementiert sind, wie diejenigen in der Win32-API. Zur Verwendung des Plattformaufrufs (Platform Invoke) müssen Sie jeden externen Verweis mit Hilfe der Schlüsselwörter Function und Lib mit einer Declare-Anweisung deklarieren. Aufrufe über Declare-Anweisungen durchzuführen, kann effizienter sein als das Aufrufen von COM-Objekten. Weitere Informationen finden Sie unter Verwenden nicht verwalteter DLL-FunktionenHier verlassen Sie die Website von Microsoft Deutschland.

 

Weitere Optimierungen

Kompilieren und Vorkompilieren

Ihr Quellcode wird vom Visual-Basic-Compiler in MSIL (Microsoft Intermediate Language) kompiliert. MSIL befindet sich in der EXE-Datei Ihrer Anwendung, die vom JIT-Compiler (Just-In-Time) der Common Language Runtime gelesen wird. Der JIT-Compiler kompiliert normalerweise jede Prozedur-MSIL in den systemeigenen Code der Plattform, sobald die Prozedur zum ersten Mal aufgerufen wird.

Es kann nützlich sein, häufig verwendete Prozeduren zu kompilieren, bevor sie aufgerufen werden. Die integrierte Entwicklungsumgebung (Integrated Development Environment, IDE) übernimmt diese Vorkompilierung für die Standardbibliotheken von Visual Basic und platziert die systemeigenen Codeversionen in einen besonderen Abschnitt des globalen Assemblycache (GAC). Dies spart Zeit, da für Visual Basic-Laufzeitfunktionen keine JIT-Kompilierung mehr erforderlich ist.

In einigen Fällen geben bestimmte Prozeduren in Ihrer Anwendung gute Kandidaten für die Vorkompilierung ab. Windows-Forms-Anwendungen verwenden z.B. viele gemeinsam genutzte Bibliotheken und rufen viele Prozeduren zur Startzeit auf. Indem Sie diese Prozeduren vorkompilieren, könnten Sie die Leistung verbessern.

Sie können Teile Ihrer Anwendung vorkompilieren, wenn Sie während der Installation ngen.exe verwenden. Beachten Sie, dass ngen.exe nicht den JIT-Compiler aufruft, sondern stattdessen einen eigenen Kompiliervorgang durchführt. Weitere Informationen finden Sie unter Native Image Generator (Ngen.exe)Hier verlassen Sie die Website von Microsoft Deutschland. Bei der Vorkompilierung Ihres Codes sollten Sie Folgendes berücksichtigen:

  • Sie können nur clientseitige Anwendungen vorkompilieren.

  • Sie können keine ASP.NET-Anwendungen vorkompilieren.

  • Die Vorkompilierung dient dazu, die Leistung Ihrer Anwendung lediglich zur Startzeit zu verbessern.

  • Die Leistung verbessert sich möglicherweise nicht merklich. Sie sollten Ihre Anwendung sowohl mit als auch ohne Vorkompilierung testen und die Geschwindigkeiten vergleichen.

  • Vorkompilierung kann die Laufzeitleistung einer häufig aufgerufenen Prozedur geringfügig reduzieren. Der Grund dafür ist, dass ngen.exe einige der Optimierungen, die der JIT-Compiler zur Laufzeit vornimmt, nicht durchführen kann.

DLLs
Das Laden einer DLL (Dynamic-Link Library) nimmt eine beträchtliche Ausführungszeit in Anspruch. Eine DLL lediglich zum Aufrufen von ein oder zwei Prozeduren zu verwenden, ist in höchstem Maße ineffizient. Sie sollten versuchen, eine so kleine Anzahl von DLLs wie möglich zu generieren, auch wenn sie dadurch recht groß werden. Das bedeutet, dass Ihre Anwendung so wenig Projekte wie möglich und große Projektmappen verwenden sollte.

Debug- und Verkaufsbuilds
Sie können die Leistung verbessern, indem Sie in einen Verkaufsbuild statt in einen Debugbuild kompilieren. Dies ermöglicht Compileroptimierungen, die die resultierende IL (Intermediate Language) kleiner und schneller machen. Diese Optimierungen erfordern jedoch wiederum Code, der das Debuggen erschwert. Debugbuilds sollten kompiliert werden, während sich die Anwendung noch in der Entwicklung befindet.

Optimieren der Anzeigegeschwindigkeit

In einer Windows-Forms-Anwendung müssen wichtige Formulare und Steuerelemente manchmal so schnell wie möglich angezeigt werden. Zur Optimierung dieses Vorgangs müssen Sie Folgendes berücksichtigen:

  • Vermeiden Sie unnötiges Neuzeichnen von Steuerelementen. Es kann hilfreich sein, die Steuerelemente auszublenden, während Sie ihre Eigenschaften einrichten.

  • Wenn Sie ein Steuerelement oder ein anderes Anzeigeelement neu zeichnen müssen, versuchen Sie, nur den neu angezeigten Bereich eines Objekts neu zu zeichnen. Dadurch muss der Benutzer weniger lange warten, bis er eine vollständige Anzeige sieht.

  • In Visual Basic .NET gibt es keine Entsprechung zum Image-Steuerelement früherer Versionen. Zum Anzeigen der meisten Grafiken müssen Sie PictureBox-Steuerelemente verwenden. Die AutoRedraw-Funktion wird ebenfalls nicht mehr unterstützt. Weitere Informationen finden Sie unter Einführung in das PictureBox-Steuerelement in Windows FormsHier verlassen Sie die Website von Microsoft Deutschland.

Optimieren der wahrgenommenen Anzeigegeschwindigkeit

Wenn der Eindruck vermieden werden soll, dass Ihre Anwendung nicht mehr ausgeführt wird, können Sie versuchen, die wahrgenommene Anzeigegeschwindigkeit zu optimieren. Der folgende Vorschlag könnte hilfreich sein:

  • Verwenden Sie Fortschrittsindikatoren, wie das ProgressBar-Steuerelement, das in der ProgressBar-Klasse im System.Windows.Forms-Namespace zur Verfügung steht. Dadurch wird dem Benutzer versichert, dass Ihre Anwendung immer noch ausgeführt wird. Weitere Informationen finden Sie unter Einführung in das ProgressBar-Steuerelement in Windows FormsHier verlassen Sie die Website von Microsoft Deutschland.

  • Während kurzer Vorgänge, die nur eine Sekunde oder weniger dauern, können Sie den Mauszeiger mit Hilfe der MousePointer-Eigenschaft des Masked Edit-Steuerelements in eine Sanduhr verwandeln.

  • Laden Sie kritische Daten, bevor sie von Ihrer Anwendung benötigt werden. Dazu gehören Formulare und Steuerelemente sowie andere Datenelemente. Es dauert zwar immer noch genauso lange, diese Elemente zu laden, doch muss der Benutzer weniger lange darauf warten, dass sie angezeigt werden, wenn er sie benötigt.

  • Wenn Sie Formulare oder Steuerelemente vorab geladen haben, lassen Sie sie ausgeblendet. Dadurch wird auch der Zeichnungsaufwand minimiert.

  • Während Ihre Anwendung auf Benutzereingaben wartet, verwenden Sie Threads und Zeitgeber, um kleine Aufgaben im Hintergrund durchzuführen. Auf diese Weise können Daten für die Anzeige vorbereitet werden, wenn der Benutzer sie anfordert.

  • Es ist häufig sinnvoll, die Geschwindigkeit der ersten Anzeigen der Anwendung zu maximieren, d.h. derjenigen, die beim ersten Laden angezeigt werden. Die folgenden Punkte sollten berücksichtigt werden:

    • Halten Sie die ersten Formulare und Steuerelemente so einfach wie möglich, um die Lade- und Initialisierungszeiten zu verkürzen.

    • Rufen Sie in jedem Formularladeereignis Me.Show als eine der ersten Codezeilen auf.

    • Vermeiden Sie es, Module zu laden, die nicht sofort benötigt werden. Achten Sie darauf, Aufrufprozeduren zu vermeiden, die solche vorzeitigen Ladevorgänge erzwingen.

  • Wenn die Anzeige Animationen enthält oder ein Anzeigeelement häufig ändert, verwenden Sie Doppel- oder Mehrfachpufferung, um das nächste Bild vorzubereiten, während das aktuelle gezeichnet wird. Die ControlStyles-Enumeration im System.Windows.Forms-Namespace wird auf viele Steuerelemente angewendet, und das DoubleBuffer-Member kann Flackern verhindern.

Reduzieren der Speicherbelegung

Es ist wichtig, die Menge des ausführbaren Codes auf einem Mindestmaß zu halten. Sie müssen u.U. auch die Speicheranforderungen Ihrer Anwendungsdaten reduzieren. Solche Reduzierungen führen häufig zu Leistungsverbesserungen, da kleinere EXE-Dateien gewöhnlich schneller ausgeführt werden, und die Vermeidung von Speicherauslagerungen erhöht die Ausführungsgeschwindigkeit. In dieser Hinsicht können Sie möglicherweise von den folgenden Empfehlungen profitieren:

  • Minimieren Sie die Anzahl der Formulare, die gleichzeitig geladen werden. Verzögern Sie das Laden eines Formulars so lange, bis es benötigt wird, sofern Sie nicht die wahrgenommene Anzeigegeschwindigkeit optimieren möchten. Wenn Sie mit einem Formular fertig sind, entladen Sie es und setzen die Variable auf Nothing.

  • Verwenden Sie so wenige Steuerelemente wie möglich für ein Formular. Verwenden Sie die kleinsten und einfachsten Steuerelemente, die Ihrem Bedarf gerecht werden. Versuchen Sie z.B., Bezeichnungen statt Textfelder zu verwenden.

  • Speichern Sie verwandte Prozeduren im selben Modul. Dadurch wird das Laden von Modulen minimiert.

  • Vermeiden Sie, größere Datentypen zu verwenden als erforderlich, insbesondere in Arrays.

  • Wenn Sie mit einer Variablen eines großen Datentyps fertig sind, setzen Sie sie auf Nothing. Dies gilt insbesondere für Zeichenfolgen, Arrays und andere potenziell große Objekte.

  • Wenn Sie mit einigen Elementen eines Arrays fertig sind, andere jedoch beibehalten müssen, verwenden Sie ReDim, um den unbenötigten Speicher an Garbage Collection (GC) freizugeben.

 

Schlussfolgerung

Wenn Sie eine Visual Basic .NET-Anwendung entwerfen und schreiben, um die Leistung zu optimieren, berücksichtigen Sie die folgenden wichtigen Punkte:

  • Konzentrieren Sie Ihre Optimierungsbemühungen auf Code, der in Schleifen und häufig aufgerufenen Prozeduren ausgeführt wird.

  • Suchen Sie die langsamsten Stellen in Ihrer Anwendung, und optimieren Sie sie, um eine für den Benutzer akzeptable Leistung zu erzielen.

  • Verwenden Sie stark typisierte Variablen, und sorgen Sie für frühe Bindungen an Object-Variablen, wenn immer dies möglich ist.

  • Setzen Sie Option Strict On und Option Explicit On.

  • Minimieren Sie die Speicherbelegung.

  • Kompilieren Sie in einen Verkaufsbuild, wenn Sie keinen Debugbuild benötigen.

  • Planen Sie Ihre Anwendung mit großen Projektmappen und so wenigen Projekten wie möglich.

  • Messen Sie die Leistung, statt einfach nur davon auszugehen, dass die eine Technik effizienter ist als die andere.

Disziplinierte Codierung mit ordnungsgemäßem Entwurf der Gesamtlogik ist der erste Schritt zur Optimierung der Leistung. Eine Optimierung ist wenig hilfreich, wenn die Anwendung nicht gut konzipiert ist.

Weitere Ressourcen

Die folgenden Ressourcen, die Teil der Visual Studio .NET-Dokumentation sind, liefern weitere Informationen zu den in diesem Artikel beschriebenen Konzepten und Programmierelementen.

Datentypen
Werttypen Hier verlassen Sie die Website von Microsoft Deutschland
Beschreibt das Konzept von Werttypen in .NET Framework.
Richtlinien für die Verwendung von WerttypenHier verlassen Sie die Website von Microsoft Deutschland
Stellt die allgemeinen .NET Framework-Richtlinien zur richtigen Verwendung von Werttypen bereit.
DatentypenHier verlassen Sie die Website von Microsoft Deutschland
Definiert den Begriff "Datentyp" und beschreibt die verschiedenen verfügbaren Typen in Visual Basic .NET.
Implizite und explizite KonvertierungenHier verlassen Sie die Website von Microsoft Deutschland
Beschreibt die implizite und explizite Datentypkonvertierung sowie die Konvertierungsschlüsselwörter.
ReDim-AnweisungHier verlassen Sie die Website von Microsoft Deutschland
Ordnet Speicherplatz für Arrayvariablen neu zu.

Deklarationen
Option Compare-Anweisung Hier verlassen Sie die Website von Microsoft Deutschland
Deklariert die Vergleichsmethode, die beim Vergleichen von Zeichenfolgendaten verwendet wird.
Auflistungen in Visual Basic .NETHier verlassen Sie die Website von Microsoft Deutschland
Bietet eine Übersicht der Auflistungen, beschreibt die Collection-Klasse, 0-basierte und 1-basierte Auflistungen sowie Index- und Schlüsselwerte und erläutert, wie Elemente hinzugefügt und entfernt werden.
Verarbeiten von Laufwerken, Ordnern und DateienHier verlassen Sie die Website von Microsoft Deutschland
Stellt die drei wesentlichen Methoden für den Zugriff auf Datenträgerdateien mit Visual Basic .NET vor.
System.IO-NamespaceHier verlassen Sie die Website von Microsoft Deutschland
Enthält Klassen und andere Typen, die synchrones und asynchrones Lesen und Schreiben auf Datenstreams und Dateien ermöglichen.
FileStream-KlasseHier verlassen Sie die Website von Microsoft Deutschland
Macht einen Stream um eine Datei zugänglich, wobei synchrone und asynchrone Lese- und Schreibvorgänge unterstützt werden.
BufferedStream-KlasseHier verlassen Sie die Website von Microsoft Deutschland
Implementiert gepuffertes Lesen und Schreiben um einen vorhandenen Stream.
Multithreading in Visual Basic .NETHier verlassen Sie die Website von Microsoft Deutschland
Beschreibt, wie mehrere Aufgaben gleichzeitig durchgeführt werden.

Operationen
Boolesche Ausdrücke Hier verlassen Sie die Website von Microsoft Deutschland
Beschreibt Ausdrücke, die die Boolean-Werte TRUE oder FALSE, Umgehungsoperatoren und Ausdrücke in Klammern in Zahlen ausdrücken.
Logische OperatorenHier verlassen Sie die Website von Microsoft Deutschland
Liefert Informationen zu Operatoren, die Boolean-Ausdrücke vergleichen und ein Boolean-Ergebnis zurückgeben.
ControlCollection-KlasseHier verlassen Sie die Website von Microsoft Deutschland
Stellt ASP.NET-Serversteuerelementen einen Auflistungscontainer für das Verwalten einer Liste der entsprechenden untergeordneten Steuerelemente bereit.
Err-ObjektHier verlassen Sie die Website von Microsoft Deutschland
Enthält Informationen über Laufzeitfehler.
ZeichendatentypenHier verlassen Sie die Website von Microsoft Deutschland
Beschreibt die Typen Char und String.
Format-FunktionHier verlassen Sie die Website von Microsoft Deutschland
Gibt eine Zeichenfolge zurück, die entsprechend den Anweisungen in einem String-Formatausdruck formatiert ist.
Die Funktionen "Asc" und "AscW"Hier verlassen Sie die Website von Microsoft Deutschland
Gibt einen Integer-Wert zurück, der den einem Zeichen entsprechenden Zeichencode darstellt.
Die Funktionen "Chr" und "ChrW"Hier verlassen Sie die Website von Microsoft Deutschland
Gibt das dem angegebenen Zeichencode zugeordnete Zeichen zurück.

Prozeduren
For...Next-Anweisungen Hier verlassen Sie die Website von Microsoft Deutschland
Beschreibt, wie ein Anweisungsblock so oft wie angegeben ausgeführt wird.
For Each...Next-AnweisungenHier verlassen Sie die Website von Microsoft Deutschland
Beschreibt, wie ein Anwendungsblock für jedes Element in einer Auflistung ausgeführt wird.
IEnumerable.GetEnumerator-MethodeHier verlassen Sie die Website von Microsoft Deutschland
Gibt eine Enumeration für ein Array zurück, um das Lesen seiner Daten zu ermöglichen.
Add-MethodeHier verlassen Sie die Website von Microsoft Deutschland
Fügt einem Visual Basic .NET-Collection-Objekt ein Member hinzu.
Item-EigenschaftHier verlassen Sie die Website von Microsoft Deutschland
Gibt ein bestimmtes Member eines Visual Basic .NET-Collection-Objekts über die Position oder den Schlüssel zurück.
ComboBox.ObjectCollection-KlasseHier verlassen Sie die Website von Microsoft Deutschland
Verwaltet die Auflistung von Elementen in einem ComboBox-Objekt, einschließlich Zeichenfolgen, Bildern und benutzerdefinierten Geschäftsobjekten.
AppDomain-KlasseHier verlassen Sie die Website von Microsoft Deutschland
Implementiert eine Anwendungsdomäne, d.h. eine isolierte Umgebung, in der Anwendungen ausgeführt werden.
Thread-KlasseHier verlassen Sie die Website von Microsoft Deutschland
Erstellt und steuert einen Thread, legt dessen Priorität fest und ruft den Status ab.
System.Reflection-NamespaceHier verlassen Sie die Website von Microsoft Deutschland
Enthält Klassen und Schnittstellen, die eine verwaltete Ansicht der geladenen Typen und ihrer Member mit der Fähigkeit zum dynamischen Erstellen und Aufrufen von Typen bereitstellen.
TypeAttributes-EnumerationHier verlassen Sie die Website von Microsoft Deutschland
Legt Visual Basic .NET-Typattribute fest, die mit der in der Datei corhdr.h definierten CorTypeAttr-Enumeration übereinstimmen.
Declare-AnweisungHier verlassen Sie die Website von Microsoft Deutschland
Deklariert Verweise auf externe Prozeduren in einer DLL (Dynamic-Link Library).

Weitere Optimierungen
Optimierungen, Konfigurationseigenschaften, Dialogfeld "-Eigenschaftenseiten" Hier verlassen Sie die Website von Microsoft Deutschland
Legt globale Einstellungen für Ihr Projekt fest. Diese Einstellungen fungieren als Standardeinstellungen und steuern für die Dauer des Lebenszyklus des Projekts die Anzeige und das Verhalten der Projektelemente.
Eigenschaftenseiten, Registerkarte "Erstellen"Hier verlassen Sie die Website von Microsoft Deutschland
Ermöglicht das Festlegen von Optionen, wie Option Strict, und benutzerdefinierten Einschränkungen, die vorhanden sind, wenn Ihr Projekt initialisiert wird.
Eigenschaftenseiten, Registerkarte "Allgemein"Hier verlassen Sie die Website von Microsoft Deutschland
Ermöglicht es Ihnen, auf Ihr Projekt zuzugreifen und dessen Namen und den Stammnamespace zu ändern.
MousePointer-Eigenschaft (ActiveX-Steuerelemente)Hier verlassen Sie die Website von Microsoft Deutschland
Gibt einen Wert zurück bzw. legt einen Wert fest, der die Art des Mauszeigers angibt, der angezeigt wird, wenn sich der Mauszeiger zur Laufzeit über einem bestimmten Teil eines Objekts befindet.
ControlStyles-EnumerationHier verlassen Sie die Website von Microsoft Deutschland
Gibt den Stil und das Verhalten eines Steuerelements an.
Label-Klasse (System.Web.UI.WebControls)Hier verlassen Sie die Website von Microsoft Deutschland
Implementiert ein Label-Steuerelement, das Text auf einer Webseite anzeigt.
Label-Klasse (System.Windows.Forms)Hier verlassen Sie die Website von Microsoft Deutschland
Implementiert ein Windows-Standardlabel, das gewöhnlich verwendet wird, um beschreibenden Text für ein Steuerelement bereitzustellen.
TextBox-Klasse (System.Web.UI.WebControls)Hier verlassen Sie die Website von Microsoft Deutschland
Erstellt ein Textfeld, in das der Benutzer Text eingeben kann, und definiert dessen Eigenschaften.
TextBox-Klasse (System.Windows.Forms)Hier verlassen Sie die Website von Microsoft Deutschland
Stellt ein Textfeld-Steuerelement von Windows dar, das dem Benutzer die Eingabe von Text in eine Anwendung ermöglicht.

Abhandlungen zur Leistung
Performance Considerations for Run-Time Technologies in the .NET Framework Hier verlassen Sie die Website von Microsoft Deutschland (in Englisch)
Untersucht verschiedene Technologien bei der Arbeit in einer verwalteten Umgebung und erläutert, wie sie sich auf die Leistung auswirken. Zu den behandelten Themen gehören Garbage Collection, der JIT-Compiler, Remoting, Werttypen und Sicherheit.
Performance Tips and Tricks in .NET ApplicationsHier verlassen Sie die Website von Microsoft Deutschland (in Englisch)
Beschreibt, wie Sie verwaltete Anwendungen für eine optimale Leistung optimieren können. Enthält Beispielcode, Entwurfsrichtlinien und sprachenspezifische Tipps.