Im Beitrag Performanceprobleme von Silverlight-Anwendungen finden und beheben bin ich bereits auf typische Performance-Probleme von Silverlight-Anwendungen eingegangen. Nach einer gestrigen WPF-Performance-Session kann ich der Liste noch einige Punkte hinzufügen, die ich natürlich mit meinen Lesern teilen möchte. Diese treffen sowohl auf Silverlight, als auch auf WPF zu.
Aufwändige Styles und Templates
Gerade bei der Verwendung von ItemsControl-Elementen in Verbindung mit Templates (ControlTemplates, DataTemplates etc.) sollten alle nicht zwingend benötigten Elemente entfernt werden. Oftmals können Daten wesentlich einfacher dargestellt werden, als dies im ersten Ansatz definiert wird. Man denke beispielsweise an ein CellTemplate, welches für eine Liste/Grid eine einzelne Zelle beschreibt. Können pro Zelle zwei Elemente entfernt werden und besitzt eine Tabelle 15 Spalten mit 1.000 Zeilen, dann entfallen 30.000 Elemente, die nicht instantiiert und vom Layout-System berücksichtigt werden müssen. Dies hat sehr große Auswirkung auf das Measuring und Arrangement des Layouts und natürlich auch auf das Scrolling. Aufzupassen ist hier vor allem auf die übermäßige Verwendung von Panels. So kann ein DockPanel in jeder Zelle einen sehr großen Aufwand verursachen. Meist sind diese jedoch nicht notwendig oder können durch einfachere Panels ersetzt werden. Über diese Optimierungen kann das Laufzeitverhalten der Oberfläche sehr stark verbessert werden.
Ressourcen im Allgemeinen
Ein weiterer – häufig vorkommender Fehler – ist ein fehlerhaftes platzieren von Ressourcen. Häufig verwendete Ressourcen sollten im Anwendungs- oder Fenster-Scope definiert werden. Oftmals werden Ressourcen direkt bei einem Element definiert. Dies kann zu erheblichen Performance-Einbußen führen. Der Hintergrund liegt in der Häufigkeit der Instantiierung. Während eine anwendungsweite Ressource nur einmal instantiiert wird, passiert dies bei der Angabe im Ressourcen-Abschnitt eines Elements bei jeder Instanziierung dieses Elementes. Wird also dieses Element beispielsweise in einem Raster von 100 mal 100 angezeigt, wird die zu verwendente Ressource 10.000 Mal erstellt. Durch eine Platzierung im Ressourcenbereich der Anwendung oder des Fensters kann dieses Verhalten vermieden werden.
Berechnete Eigenschaften
Gebundene Eigenschaften sollten nicht berechnet sein. D.h. der jeweilige Getter sollte keine Berechnung implementieren, sondern ausschließlich existierende Werte zurück liefern. Handelt es sich um einen berechneten Wert, der sich aus weiteren Eigenschaften zusammen setzt, dann sollte die Berechnung beim Setzen der geänderten Werte (also im jeweiligen Setter) durchgeführt werden. Somit wird die Berechnung weniger häufig durchgeführt und belastet die reine Anzeige von Werten nicht. Je weniger in einem Getter passiert, desto besser ist das Anzeigeverhalten. Zudem akzeptiert der Benutzer eher längere Wartezeiten beim Speichern/Setzen von Werten.
Fehlerhafte Bindings
Binding-Expressions die nicht aufgelöst werden können, verursachen bei häufigem Vorkommen einen erhöhten Performance-Verlust. In kleinen Szenarien hat dies wenig bis keine Auswirkung. Bei der Bindung an eine Liste mit vielen Spalten und Zeilen kann dies aber schon einiges ausmachen. Generell sollten fehlerhafte Bindings daher vermieden werden, vor allem, wenn viele Daten ins Spiel kommen.
ScrollBarVisibility=Auto
Diese Eigenschaft ist für die Elemente RichTextBox, ScrollViewer und TextBox definiert. Für das ListBox-Element steht diese Eigenschaft als Attached Property zur Verfügung. Das Setting Auto sollte an dieser Stelle vermieden bzw. nur dann eingesetzt werden, wenn Platzprobleme bestehen. Stattdessen ist es besser, den Scrollbalken gänzlich auszublenden (Eigenschaftswerte Hidden/Disabled) oder ständig anzuzeigen (Visible). Dadurch entfällt die Prüfung, ob der Scrollbalken anzuzeigen ist, oder nicht.
Einige dieser Punkte können relativ schnell behoben werden, sollten aber dennoch bereits bei der Implementierung beherzigt werden, um spätere Komplikationen (beispielsweise mit dem Kunden) zu vermeiden. Wie generell bei Performance-Issues sollten Stellen, die häufigen Aufrufen unterliegen besonders sorgfältig bedacht und implementiert werden.
Ein Frage hätte ich zu dem Thema ‘Aufwändige Styles und Templates’. Wenn ich die gemeinsamen Resourcen nur auf der obersten Ebene, also dem Rootcontrol, einbinde, bekomme ich später bei der Ausführung Exceptions in den untergeordneten Controls. Muss ich hier von ‘StaticResource’ auf ‘DynamicResource’ umstellen?
StaticResource und DynamicResource beschreiben nur die Art, wie die entsprechende Ressource tatsächlich behandelt wird. Das hat aber nichts mit dem Ort, an dem sie definiert wurde, zu tun.
Was genau bei dir schief läuft, kann in erster Linie nur die Message der Exception bzw. deren Typ beschreiben. Das heißt, nur mit der verfügbaren Information kann ich dazu keine Aussage tätigen.