2009-04-28 17 views
40

Wiele typów w WPF pochodzi z Freezable. Zapewnia niezmienność zmiennym obiektom POCO i, pozornie, pozwala na poprawę wydajności w pewnych sytuacjach.W jakich sytuacjach zamrażanie obiektów WPF znacząco wpływa na wydajność?

Czy ktoś odkrył, że zamrożenie obiektów w aplikacji WPF znacznie poprawiło wydajność? Jeśli tak, to które przedmioty dawały największą różnicę wydajności podczas zamrażania?

(Zauważ, że napisali similar but different question zbyt)

Odpowiedz

43

Możesz być zainteresowany w moich doświadczeniach z Freezable:

kiedyś napisał przeglądarki PDF używając muPdf który renderuje bitmap, abym render z WPF. To, co bardzo pomaga w wydajności, polega na tym, że mogę renderować mapy bitowe strony na wątku w tle, zamrażać je, a następnie przekazywać do wątku interfejsu użytkownika. Fajnie, że WPF nie kopiuje obrazu, aby go zamrozić, ale umiejętność wykonania całego tego przygotowania na wątku tła była dla mnie kluczową korzyścią.

Z tego co rozumiem, wszystkie wizualizacje muszą zostać zamrożone, aby mogły być bezpiecznie renderowane przez wątek renderowania WPF. Jeśli wyrenderujesz duże, niezamrożone efekty wizualne, zostaną one sklonowane do zamrożonych, gdy renderuje je WPF. Jeśli wcześniej zamrozisz statyczne bitmapy, WPF może udostępnić wskaźnik wątkowi renderowania bez klonowania. Niezamrożone obiekty mogą nawet zostać wielokrotnie skopiowane, jeśli WPF nie jest świadomy, że obiekt został zmieniony po ostatnim renderowaniu. Zamrożone obiekty eliminują potrzebę tego kopiowania.

+0

Bardzo pomocna dziękuję. Nie wiedziałem, że możesz to zrobić w wątku w tle, powinno znacznie pomóc jednej z moich aplikacji! – Kelly

16

Te potencjalne wycieki pamięci może się zdarzyć, jeśli używasz kontroli obrazu (i nie stosować metody zamrażania):

a) użyć BitmapImage jako obrazu źródło i nie zwalniaj BitmapImage:

static BitmapImage bi1 = new BitmapImage(new Uri("Bitmap1.bmp",UriKind.RelativeOrAbsolute)); 
m_Image1 = new Image(); 
m_Image1.Source = bi1; 
//bi1.Freeze() 
//if you do not Freeze, your app will leak memory. 
MyStackPanel.Children.Add(m_Image1); 

b) można przypisać wiele BitmapImage jako źródło obrazu i nie zwalniaj wszystkich BitmapImage użyłeś (podobny do (a)). Ten wprowadzony w .NET 3.5:

static BitmapImage bi1 = new BitmapImage(new Uri("Bitmap1.bmp", 
UriKind.RelativeOrAbsolute)); 
static BitmapImage bi2 = new BitmapImage(new Uri("Bitmap2.bmp", 
UriKind.RelativeOrAbsolute)); 
bi2.Freeze(); 
m_Image1 = new Image(); 
//bi1.Freeze() 
//even though you are really using bi2 for Image Source, 
//you also need to Freeze bi1 it to avoid leak 
m_Image1.Source = bi1; // use un-frozen bitmap, which causes the leak 
m_Image1.Source = bi2; // use frozen bitmap 
MyStackPanel.Children.Add(m_Image1); 

Źródło: WPF Performance

+0

Zakładam, że odwołujesz się do scenariuszy opisanych tutaj: https://blogs.msdn.microsoft.com/jgoldb/2008/02/04/finding-memory-leaks-in-wpf-based-applications/ (you powinien podać konkretny link w twojej odpowiedzi). Myślę, że warto podkreślić, że zamrożenie mapy bitowej w tych scenariuszach jest po prostu obejściem tego, co zasadniczo można uznać za zły pomysł, tj. Za pomocą pola 'statycznego' do przechowywania mapy bitowej, którą następnie odniesione w obiekcie 'Obraz'. –

+0

Obiekt 'Obraz' musi koniecznie zasubskrybować zmiany, dlatego bitmapa zachowuje silne odniesienie, uniemożliwiając GC' obrazu' i oczywiście wszystko, co subskrybuje zdarzenia _its_ change. Jest to naprawdę szerszy problem, który ludzie piszący o zdarzeniach muszą być świadomi: pole 'statyczne' może rootować całe drzewo obiektów, jeśli obiekty te są pośrednio przywoływane przez pola zdarzeń. Zamrażanie bitmapy jest dobre z innych powodów, ale lepszym rozwiązaniem tego problemu jest nie umieszczanie obiektu bitmapowego w statycznym polu, gdy już go nie potrzebujesz (np. Gdy okno jest zamknięte). –

12

Chociaż już zaakceptowałeś odpowiedź, chciałem tylko zarejestrować inną wersję odpowiedzi, która pomogła mi lepiej.

Od MSDN (drobne zmiany):

Jeśli było modyfikować kontroli gospodarstwa odniesienie do niezagospodarowanych zasobów niskim poziomie (np: Brush), każda zmiana będzie musiał regenerować te obiekty niskopoziomowe!

Klasa freezable umożliwia pędzelowi znalezienie odpowiednich obiektów niskiego poziomu i aktualizację , gdy ulegnie zmianie. Gdy ta funkcja jest włączona, pędzel zostaje oznaczony jako "niezamrożony" jako .

Metoda Freezeable's Freeze umożliwia wyłączenie tej automatycznej aktualizacji zdolności. Możesz użyć tej metody, aby uczynić pędzel "zamrożonym" lub niemodyfikowalnym. W ten sposób poprawiając wydajność.

A, kod wyjaśnić Zastosowanie:

  Button myButton = new Button(); 
      SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow); 

      if (myBrush.CanFreeze) 
      { 
       // Makes the brush unmodifiable. 
       myBrush.Freeze(); 
      }     
      myButton.Background = myBrush;   
      if (myBrush.IsFrozen) // Evaluates to true. 
      { 
       // If the brush is frozen, create a clone and modify the clone. 
       SolidColorBrush myBrushClone = myBrush.Clone(); 
       myBrushClone.Color = Colors.Red; 
       myButton.Background = myBrushClone; 
      } 
      else 
      { 
       // If the brush is not frozen, it can be modified directly. 
       myBrush.Color = Colors.Red; 
      } 
+0

Wiem, że to stary post, ale link do artykułu MSDN byłby miły. – PeterM

+0

Zaktualizowano. dzięki. –

2

opracowałem wysokiej wydajności aplikacji przeglądarki zdjęć. Mieliśmy kodu na back-end, który stworzył nową bitmapę każdą klatkę i napisał, że bitmapy na ekranie tak:

Writeablebitmap wb = new WriteableBitmap(); 
// < code to set the wb pixel values here > 

// Push the bitmap to the screen 
image.Source = wb; 

Podczas testów zauważyliśmy, że nie było straszne migotanie idąc 30+ FPS z umiarkowanie -sized zdjęcia (1080p). Poprawka? Po prostu zamień mapę bitową przed ustawieniem jej na obraz. Źródło. Nigdy więcej błędu związanego z zabijaniem produktów. Obecnie staram się zamrozić wszystko, co mogę.

Powiązane problemy