1

Closed

Performance. Data binding updates to DetailsPage.

description

Hi,
 
First, I should say I am super impressed with the OneBusAway. I think OneBusAway is super advanced
when it comes to performance considerations. I like a lot how you guys did all that caching and
async events.
 
I believe there is a potential performance problem that has a very easy fix (just add
“this.RouteInfo.DataContext = null;” in method OnNavigatedFrom() of class DetailsPage).
 
This is what happens: each time when I navigate to a DetailsPage, a DetailsPage object is created.
When I navigate away from the details page, the object is no longer accessible to the user. However,
the object is still live in memory (calling GC.Collect()does not remove the object).
 
The problem is that DetailsPage has a data binding to “CurrentViewState.CurrentStop.name”,
declared in DetailsPage.xaml as:
 
<TextBlock Grid.Row="1" x:Name="RouteInfo" Foreground="{StaticResource OBAForegroundBrush}" Text="{Binding Path=CurrentViewState.CurrentStop.name}" FontSize="{StaticResource PhoneFontSizeMedium}"/>
 
Thus, each time when the CurrentViewState.CurrentStop changes, all the DetailsPage objects
(including the ones that are no longer accessible after navigating away from them) get
updated via data binding.
 
This is wasteful, because updating via data binding is expensive, and it is performed for
objects that are no longer accessible for the user. Since the objects do not get garbage
collected, the more you navigate, the more of these objects you have. For example, I got
up to 36 of these objects (and thus 36 data binding updates for one change of
CurrentViewState.CurrentStop), by navigating to 36 details pages (I stopped due to time
constraints). I will inline at the end of this email more details on how to reproduce this
behavior.
 
Was this behavior intended for some purpose that I am missing? If not, can you please
confirm that this is what happens and that the fix I mentioned solves this problem?
 
Thanks!
 
Adrian

 

In class Stop, put a Debug.WriteLine(“NAME”) inside the getter public string name { get; set; }.
This will enable you to see when Stop.name is called due to data binding.
 
Put a Debug.WriteLine(“-----”) inside the DetailsPage() constructor of DetailsPage. This will help
see the WriteLine that get triggered before actually creating a DetailsPage.
 
Open application.
Goto Stops pivot
push one item (e.g., the first stop) in the stop list at the bottom of the page (i.e., not the map).
  When pushing one item, do not scroll the list. Scrolling calls “name”, which shows spurious 
  “NAME” prints.
-> this gets you to the details page
-> you will not see “NAME:” printed before “-----”, because there are currently no DetailsPage
 objects created
You are now on the details page. Push back button
-> this gets you back to Stops pivot
Push again one item in the stop list at the bottom of page (again, do not scroll the list)
-> you will see one “NAME” printed before “-----”. This is the previous page object (that you
  navigated away from) that is still in memory. It gets updated via binding because when 
  you navigate to a Details page, the CurrentViewState.CurrentStop changes
You are now on the details page. Push back button
-> this gets you back to the Stops pivot
 
Repeat last two steps. Third time you will see two “NAME:” printed before “-----”,forth time
you will see three a “NAME:” printed before “-----”, and so on.
 
You can put a GC.Collect(); in OnNavigatedFrom() of class DetailsPage. You will see that the
above behavior still happens, i.e., the objects do not get garbage collected (I tried up to
36 times, and still get this behavior, i.e., 36 “NAME” prints). The above behavior disappears
if you put a “this.RouteInfo.DataContext = null;” in OnNavigatedFrom() of class DetailsPage,
i.e., you disable data binding.
 
I found very useful to use the “Clear All” button in the Output window in Microsoft Visual
Studio. By clearing all previous prints (generated by a Debug.WriteLine), it is easier for me
to see the prints generated for my current action.
 
If in addition to Debug.WriteLine(“NAME”), you also print the actual name, you will see that
each time the printed name (and thus the name being updated via binding) is the name
of the stop item in the stop list that you pushed. I.e., as expected, all objects get updated
with the current name.
Closed Jul 26, 2011 at 3:58 AM by Sinorm

comments

mwfriedm wrote Jul 25, 2011 at 10:59 PM

Proposed fix sounds reasonable. Need to test that navigating back to the details page creates a new instance -- if it reuses the existing instance, then we would need to reset the Binding in OnNavigatedTo().

mwfriedm wrote Jul 26, 2011 at 2:23 AM

Yeah, that's what I was afraid of. The proposed fix breaks the tombstone scenario; upon resume, you get a blank title block because it's not bound to anything.

mwfriedm wrote Jul 26, 2011 at 3:10 AM

After further experimentation, I think the right thing to do is to clear these references in the page's Unloaded event. It looks like we have the same problem with all of the bound properties hanging off CurrentViewState -- another motivation to get rid of that global state object.

Sinorm wrote Jul 26, 2011 at 3:36 AM

For 2.6 let's go ahead and hack a fix for the details page since that is the page most likely to build up background copies. This will be properly fixed in 3.0 by getting rid of the CurrentViewState as you mentioned.

wrote Jul 26, 2011 at 3:37 AM

Sinorm wrote Jul 26, 2011 at 3:53 AM

Also, Adrian's proposed fix works fine for NoDo devices because the page is always recreated after tombstoning. It only breaks the Mango version of the app since apps are simply paused instead of destroyed and recreated.

wrote Jul 26, 2011 at 3:54 AM

wrote Jul 26, 2011 at 3:55 AM

wrote Jul 26, 2011 at 3:58 AM

wrote Jul 26, 2011 at 3:58 AM

Resolved with changeset 77393.

wrote Feb 13, 2013 at 7:58 PM

wrote May 16, 2013 at 2:06 AM