Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ElementName vs. RelativeResource?

What of the following TextBlocks' Bindings costs more performance:

<Window  
  x:Name="Me"
  x:Class="MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:src="clr-namespace:WpfApplication1" 
  Title="MainWindow">
  <StackPanel>
    <TextBlock Text="{Binding Title, ElementName=Me}"/>
    <TextBlock Text="{Binding Title, RelativeSource={RelativeSource AncestorType={x:Type src:MainWindow}}}"/>
  </StackPanel>    
</Window>

I am sure my question might take different when the TextBlocks are in a high nesting level having many siblings and ancestors.

Considerations

(based on personal thoughts only, I might be wrong in each particular one!):

  • ElementName:

    • Might search and compare current element to more control, thru all its children, siblings, uncles and great uncles including ancestors (maybe there is a HashTable of all the registered names?)
    • Getting a Name property of a control should cost less performance than calling GetType.
    • Comparing a string is cheaper than comparing types, especially when you know that most of the controls don't even have their Name set.
  • FindAncestor:

    • Will only iterate thru ancestors, not siblingls 'uncles', 'cousins' etc.
    • Most likely uses GetType to determine ancestor type; GetType costs more performance then a simple Name property getter (maybe DPs are different?)
like image 703
Shimmy Weitzhandler Avatar asked Nov 30 '10 18:11

Shimmy Weitzhandler


1 Answers

It's usually a terrible idea to try to answer this sort of thing by arguing about which you think will be faster. Far better to construct an experiment to measure it.

I modified your setup a little - I put the relevant Xaml into a UserControl, and bound to the Name property since UserControl doesn't have a Title property. I then wrote some code to create a new instance of the control and add it to the UI, and used the Stopwatch to measure the time taken to construct and load it. (I start the timing just before constructing the user control, and I stop just after the user control raises its Loaded event.)

I run this code from a DispatcherTimer 20 times a second so I can take lots of measurements in the hope of reducing experimental error. To minimize distortions due to debugging and diagnostic code, I'm running in a Release build, and I only calculate and print the average after 2000 iterations have completed.

After 2000 iterations, the ElementName approach averages 887us.

After 2000 iterations, the RelativeSource approach averages 959us.

So ElementName is, in this particular experiment, slightly quicker than RelativeSource. Loading a trivial UserControl with just a Grid and one TextBlock where there's only one named element, the ElementName approach looks to take 92% of the time to load that the RelativeSource approach takes.

Of course, I'm measuring a small, artificial example here. The performance of the ElementName approach might vary based on how many named elements are in scope. And there may be other unanticipated factors that might produce completely different results in real scenarios. So I would recommend performing similar measurements in the context of a real application if you want to get a better picture.

I repeated the experiment with 10 TextBlocks instead of 1. ElementName then averaged 2020us while the RelativeSource approach averaged 2073us, again over 2000 iterations for both tests. Weirdly, there's a smaller difference here, not just in relative terms, but in absolute terms - the one-element examples showed a difference of 72us, where the ten-element examples showed a difference of 53us.

I'm starting to suspect that I'm causing more variability by running my tests on my main machine, rather than one carefully configured with as little stuff as possible to minimize noise.

One more variation: still with 10 bound text blocks, I added ten more empty, unbound, named text blocks to the user control. The idea here was to introduce more named things - ElementName now has to locate a named item within 11 named things. The average for ElementName is now 2775us. The RelativeSource approach with these extra 10 named elements came out at 3041us.

Again, I suspect variability on my desktop machine here - it seems weird that RelativeSource has performed significantly worse here than in the scenario that should have been more to ElementName's advantage.

Anyway, what does seem reasonably clear is that the cost of loading here is far more sensitive to the number of elements than it is to which style of binding you use. There is apparently a small advantage to ElementName but small enough (and with weird enough results) to cast suspicion on the validity of concluding that it is necessarily faster.

So we could construct more careful experiments to get a better picture. But in my view, if you can't conclusively demonstrate a significant difference in performance when running on an ordinary computer, then it's basically a waste of time arguing about which is quicker.

So in conclusion: performance is the wrong thing to focus on here. Pick whichever makes for more readable code.

like image 84
Ian Griffiths Avatar answered Oct 17 '22 21:10

Ian Griffiths