Colin Eberhardt's Technology Adventures

Windows Phone 7 Performance Measurements – Emulator vs. Hardware

February 11th, 2011

This blog post presents a few performance measurements that detail the relative performance of ItemsControl, ListBox and manual addition of elements to the UI. These performance measurements are also compared when ran on the emulator and the real hardware.

My early experiences of moving from the WP7 emulator to the real hardware revealed that performance is something that needs to be considered when developing an application. The complexity of the UI has a noticeable impact on the time to render a page. These findings lead me to develop the DeferredLoadContentControl which I blogged about a few days ago. In this blog post I want to explore various ways in which a UI may be constructed and their relative performance.

The test I constructed was quite simple, the MainPage has a number of buttons, each of which navigates to a new page. When the navigation is initiated, the timer starts, and when the Loaded event fires on the target page, the timer is stopped. This accurately records the wait that the user experiences in navigating.

My tests each construct a number of buttons and add them to the UI. Each test is run twice, once to construct 10 buttons, and another time to create 100. The tests are briefly described below:

  1. Buttons created in XAML
  2. Buttons created in code-behind and added to the visual tree.
  3. Buttons created via a ListBox (bound to a list of strings).
  4. Buttons created in code and added to a ListBox as its ItemsSource.
  5. Buttons created via a ItemsControl (bound to a list of strings), with a StackPanel as the ItemsPanel.
  6. Buttons created via a ItemsControl (bound to a list of strings), with a VirtualizingStackPanel as the ItemsPanel.
  7. And finally, an empty page!

Each test was performed 5 times, with an average result taken. Here are my findings, where the tests are being executed on a Samsung Omnia WP7 device:

I also ran the tests for populating the UI with 100 buttons on the emulator:

My conclusions are as follows:

  • Constructing the UI in code-behind or via XAML takes the same amount of time. Use whichever works best for you.
  • Using an ItemTemplate to construct the buttons doesn’t have a performance impact, taking the same amount of time to render the UI as if the buttons are added directly as children of the ListBox (or other items control).
  • Virtualization makes the rendering of a large number of items much faster, 1322ms vs. 685ms, for the ItemsControl. NOTE: The ListBox default template uses the VirtualizingStackPanel.
  • Virtualization makes the rendering of a small number of items a little bit slower.
  • An ItemsControl (when virtualizing) is faster at rendering 100 buttons than a ListBox, 982ms vs. 685ms.
  • The emulator is approximately four times faster than the real hardware!

I will certainly use the above information to guide the way that I develop WP7 applications, hopefully this data will help others too.

A few take-home messages …

Firstly, because the ItemsControl (when virtualizing) is faster than the ListBox, don’t use a ListBox for navigation … I have seen a number of examples where a ListBox is used to render items such as email messages, with the SelectionChanged event being used to initiate navigation. Use an ItemsControl, and handled the bubbled events instead!

Secondly, use techniques such as the DeferredLoadContentControl, or the DeferredLoadListBox to reduce the amount of UI elements added to the screen before its initial render.

Finally, test on real hardware!

You can download the code I used to make these measurements here: WindowsPhonePerformanceTests.zip

Regards,
Colin E.

Visiblox, Visifire, DynamicDataDisplay – Charting Performance Comparison

December 10th, 2010

A few weeks ago I published a blog post which compared the performance of the Visiblox charts and the Silverlight Toolkit charts. The results indicated that the Visblox charts are considerably faster than the Toolkit charts, however Microsoft’s David Anson did point out that the Toolkit charts were not designed with performance in mind, and that a comparison would be more fair if the ‘FastLineSeries’ series that has been on the TODO list for a while were implemented.

UPDATE: I have published a more up-to-date and extensive test of WPF charting components in a more recent blog post. This new test looks at how raster-graphics can be used to significantly enhance performance.

I have been asked by a few people to extend the performance test to include a few other Silverlight charts to see how they compare. I have refactored the test code so that different charts can be plugged in more easily. This not only makes it easier to test the performance of other charting components, but has also allowed me to compare the different charting APIs more easily.

This blog post compares the performance of Visiblox, Visifire, Silverlight Toolkit and Dynamic Data Display (D3) charts. I also compared a few other charting components, however the terms of their trial license prohibit me from publishing the results.

A summary of the performance measurement is given below, where each chart is used to plot rapidly changing data, with the framerate being used as a measure of performance.

The test clients running with each of the different charts are shown below:

Visiblox Chart Visifire Chart
Silverlight Toolkit Chart D3 Chart

[cheetah image courtesy of flickrfavorites, snail image courtesy of Per Ola Wiberg, tortoise image courtesy of wwarby, horse image courtesy of Linnéa Gröndalen]

We’ll look at the XAML markup for each chart and the code-behind used to add data to each chart in the following sections:

Visiblox

The XAML markup for the Visiblox charts is reasonably concise, with the X & Y axes configured and styled and the three LineSeries added to the chart:

<UserControl.Resources>
  <Style TargetType="Line" x:Key="GridLineStyle">
    <Setter Property="Stroke" Value="#dddddd" />
    <Setter Property="StrokeThickness" Value="1" />
  </Style>
</UserControl.Resources>
 
<Grid x:Name="LayoutRoot" Background="White">
  <chart:Chart x:Name="chart"
                  LegendVisibility="Collapsed"
                  Margin="5,0,5,5">
    <chart:Chart.XAxis>
      <!-- a 'hidden' X axis -->
      <chart:LinearAxis ShowAxis="False"
                            GridlineStyle="{StaticResource GridLineStyle}"/>
    </chart:Chart.XAxis>
    <chart:Chart.YAxis>
      <!-- A Y axis with labels within the chart area -->
      <chart:LinearAxis ShowLabels="True"
                            LabelsPosition="Inside"
                            GridlineStyle="{StaticResource GridLineStyle}"
                            ShowMajorTicks="False"
                            ShowMinorTicks="False"/>
    </chart:Chart.YAxis>
    <chart:Chart.Series>
      <!-- the series used to render the RGB components -->
      <chart:LineSeries LineStroke="#A00"/>
      <chart:LineSeries LineStroke="#0A0"/>
      <chart:LineSeries LineStroke="#00A"/>
    </chart:Chart.Series>
  </chart:Chart>
</Grid>

The code-behind is also reasonably concise with the list of DataPoint objects returned by the test-harness converted into Visiblox DataSeries via a Linq Select projection.

protected override void RenderDataToChart(List<List<Histogram.DataPoint>> rgbData)
{
  _chart.Chart.Series[0].DataSeries = ListToSeries(rgbData[0]);
  _chart.Chart.Series[1].DataSeries = ListToSeries(rgbData[1]);
  _chart.Chart.Series[2].DataSeries = ListToSeries(rgbData[2]);
}
 
private DataSeries<double, double> ListToSeries(List<Histogram.DataPoint> data)
{
  return new DataSeries<double, double>(data.Select(pt => 
    new DataPoint<double, double>(pt.Location, pt.Intensity)));
}

Visifire

Visifire also has a reasonably concise XAML markup, however in order to provide maximum performance, there are a number of chart features that need to be disabled. The Visifire charts have a Line series which could be used for this comparison, however they recently released a QuickLine series type which removes certain features to provide better performance.

<Grid x:Name="LayoutRoot" Background="White">
  <vc:Chart x:Name="chart"
                AnimationEnabled="False"
                ScrollingEnabled="False">
 
    <vc:Chart.Legends>
      <vc:Legend Enabled="False"/>
    </vc:Chart.Legends>
 
    <!-- the series used to render the RGB components -->
    <vc:Chart.Series>
      <vc:DataSeries RenderAs="QuickLine"
                      Color="#A00" LineThickness="1"
                      MarkerEnabled="False" ShadowEnabled="False" />
      <vc:DataSeries RenderAs="QuickLine"
                      Color="#0A0" LineThickness="1"
                      MarkerEnabled="False" ShadowEnabled="False" />
      <vc:DataSeries RenderAs="QuickLine"
                      Color="#00A" LineThickness="1"
                      MarkerEnabled="False" ShadowEnabled="False" />
    </vc:Chart.Series>
 
    <!-- a 'hidden' X axis -->
    <vc:Chart.AxesX>
      <vc:Axis Enabled="False">
        <vc:Axis.Grids>
          <vc:ChartGrid Enabled="True"
                        LineColor="#ddd" LineThickness="1"/>
        </vc:Axis.Grids>
      </vc:Axis>
    </vc:Chart.AxesX>
 
    <vc:Chart.PlotArea>
      <vc:PlotArea ShadowEnabled="False"/>
    </vc:Chart.PlotArea>
  </vc:Chart>
</Grid>

The code-behind required to add data to the chart is straightforward. However, you cannot simply create a new DataPointCollection and replace the current Series.DataPoints, instead you have to clear the existing collection, then rebuild it.

protected override void RenderDataToChart(List<List<Histogram.DataPoint>> rgbData)
{
  AddDataToSeries(_chart.Chart.Series[0], rgbData[0]);
  AddDataToSeries(_chart.Chart.Series[1], rgbData[1]);
  AddDataToSeries(_chart.Chart.Series[2], rgbData[2]);
}
 
private void AddDataToSeries(DataSeries series, List<Histogram.DataPoint> list)
{
  series.DataPoints.Clear();
  foreach (var pt in list)
  {
    series.DataPoints.Add(new Visifire.Charts.DataPoint()
    {
      XValue = pt.Location,
      YValue = pt.Intensity
    });
  }
}

Silverlight Toolkit

The Silverlight Toolkit XAML is quite verbose, mostly due to the performance enhancements described in my previous blog post.

<UserControl.Resources>
  <Style TargetType="dataVis:Legend" x:Key="CollapsedLegendStyle">
    <Setter Property="Visibility" Value="Collapsed"/>
    <Setter Property="Width" Value="0"/>
  </Style>
 
  <Style TargetType="Control" x:Key="CollapsedStyle">
    <Setter Property="Visibility" Value="Collapsed"/>
  </Style>
 
  <ControlTemplate x:Key="SimplifiedDataPoint" TargetType="tk:LineDataPoint">
  </ControlTemplate>
</UserControl.Resources>
 
<Grid x:Name="LayoutRoot" Background="White">
  <tk:Chart x:Name="chart"
        LegendStyle="{StaticResource CollapsedLegendStyle}"
        TitleStyle="{StaticResource CollapsedStyle}">
 
    <!-- define the line series -->
    <tk:LineSeries ItemsSource="{Binding}"   
                TransitionDuration="0"
                DependentValueBinding="{Binding Intensity}" 
                IndependentValueBinding="{Binding Location}">
      <tk:LineSeries.DataPointStyle>
        <Style TargetType="tk:LineDataPoint">
          <Setter Property="Visibility" Value="Collapsed"/>
          <Setter Property="Template" Value="{StaticResource SimplifiedDataPoint}"/>
          <Setter Property="Background" Value="#A00"/>
        </Style>
      </tk:LineSeries.DataPointStyle>
    </tk:LineSeries>
 
    <tk:LineSeries ItemsSource="{Binding}"   
                TransitionDuration="0"
                DependentValueBinding="{Binding Intensity}" 
                IndependentValueBinding="{Binding Location}">
      <tk:LineSeries.DataPointStyle>
        <Style TargetType="tk:LineDataPoint">
          <Setter Property="Visibility" Value="Collapsed"/>
          <Setter Property="Template" Value="{StaticResource SimplifiedDataPoint}"/>
          <Setter Property="Background" Value="#0A0"/>
        </Style>
      </tk:LineSeries.DataPointStyle>
    </tk:LineSeries>
 
    <tk:LineSeries ItemsSource="{Binding}"   
                TransitionDuration="0"
                DependentValueBinding="{Binding Intensity}" 
                IndependentValueBinding="{Binding Location}">
      <tk:LineSeries.DataPointStyle>
        <Style TargetType="tk:LineDataPoint">
          <Setter Property="Visibility" Value="Collapsed"/>
          <Setter Property="Template" Value="{StaticResource SimplifiedDataPoint}"/>
          <Setter Property="Background" Value="#00A"/>
        </Style>
      </tk:LineSeries.DataPointStyle>
    </tk:LineSeries>
 
    <!-- configure the axes -->
    <tk:Chart.Axes>
      <tk:LinearAxis Orientation="X" Height="0">
      </tk:LinearAxis>
    </tk:Chart.Axes>
  </tk:Chart>
</Grid>

However, the code-behind is the most concise of all the charts I tested because the chart databinds directly to the properties of the DataPoint business objects.

protected override void RenderDataToChart(List<List<DataPoint>> rgbData)
{
  ((LineSeries)_chart.Chart.Series[0]).ItemsSource = rgbData[0];
  ((LineSeries)_chart.Chart.Series[1]).ItemsSource = rgbData[1];
  ((LineSeries)_chart.Chart.Series[2]).ItemsSource = rgbData[2];
}

Dynamic Data Display

The D3 charts are quite different to the others which I have tested. These charts were developed by a Microsoft team based in Russia and are released on codeplex. They claim outstanding performance with large volumes of data, and are often recommended on sites such as stackoverflow when questions relating to charting performance arise.

The XAML markup for the D3 charts is pretty minimal!

<Grid x:Name="LayoutRoot" Background="White">
  <d3:ChartPlotter Name="plotter">
  </d3:ChartPlotter>
</Grid>

However, this is because the D3 charts do not support configuration via markup. It took me quite a while to work out how to configure the charts in code-behind, the D3 APIs are not very intuitive. I am not the only one to have noticed this; Lee Campbell, in his “WPF Charting Comparisons” blog post stated ” … it took me hours of reading forums, looking at samples and coding to just get my Model showing on the screen”. Oh dear!

I had to resort to navigating the visual tree (using Linq to VisualTree) to locate the pan and zoom controls which are added to the chart by default, and remove them.

However, in fairness, the Silverlight version of D3 charts is a partial port of the WPF version, but both share a similar programmatic style of usage.

The following code is used to configure the chart in code-behind:

public DDDChart()
{
  InitializeComponent();
 
  this.Loaded += new RoutedEventHandler(DDDChart_Loaded);
}
 
private LineGraph InitGraph(Color color)
{
  var animatedX = new List<double>();
  var animatedY = new List<double>();
 
  // add some points, otherwise we get some layout-related exception
  animatedX.Add(0);
  animatedY.Add(0);
 
  // create the X & Y sources
  _xSource = new EnumerableDataSource<double>(animatedX);
  _xSource.SetXMapping(x => x);
  _ySource = new EnumerableDataSource<double>(animatedY);
  _ySource.SetYMapping(y => y);
 
  // Adding graph to plotter
  var graph = new LineGraph(new CompositeDataSource(_xSource, _ySource), "");
  graph.LineColor = color;
  graph.LineThickness = 1;
  plotter.Children.Add(graph);
 
  return graph;
}
 
private void DDDChart_Loaded(object sender, RoutedEventArgs e)
{
  // remove the mouse pan and zoom controls
  var navigationControl = plotter.Children.OfType<MouseNavigation>().Single();
  plotter.Children.Remove(navigationControl);
 
  var zoomControl = plotter.Descendants().OfType<buttonsNavigation>().Single();
  ((Panel)zoomControl.Ancestors().First()).Children.Remove(zoomControl);
 
  _graphOne = InitGraph(Color.FromArgb(255,200,0,0));
  _graphTwo = InitGraph(Color.FromArgb(255, 0, 200, 0));
  _graphThree = InitGraph(Color.FromArgb(255, 0, 0, 200));
 
  // Force everything plotted to be visible
  plotter.FitToView();
 
  plotter.Legend.Visibility = Visibility.Collapsed;
 
}

The following code is used to change the data for each series. Again, the D3 APIs are a little complex. Also, the chart does not have a mode where the X & Y axes compute their range based on the data. Instead, this is performed programatically via FitToView.

protected override void RenderDataToChart(List<List<DataPoint>> rgbData)
{
  RenderDataToGraph(_chart.LineGraphR, rgbData[0]);
  RenderDataToGraph(_chart.LineGraphG, rgbData[1]);
  RenderDataToGraph(_chart.LineGraphB, rgbData[2]);
 
  // re-scale the chart based on the rendered data
  _chart.Plotter.FitToView();
}
 
private void RenderDataToGraph(LineGraph graph, List<DataPoint> histogram)
{
  var source = graph.DataSource as CompositeDataSource;
 
  // obtain the two sources which the composite is composed of
  var xSource = source.DataParts.ElementAt(0) as EnumerableDataSource<double>;
  var ySource = source.DataParts.ElementAt(1) as EnumerableDataSource<double>;
 
  var dataX = new List<double>();
  var dataY = new List<double>();
  foreach(var point in histogram)
  {
    dataX.Add(point.Location);
    dataY.Add(point.Intensity);
  }
  xSource.Data = dataX;
  ySource.Data = dataY;
}

Conclusions

In summary, the Visiblox and Visifire charts seem to have the cleanest API, with concise XAML markup to define the charts and little code-behind required to update the series. The Silverlight Toolkit charts XAML markup is a bit verbose, mostly due to performance optimisation. Finally, the D3 charts have a complex and difficult to follow API. In terms of performance, the Visiblox and D3 charts give similar results, the Visifire charts are a little behind with, with a frame rate that is approximately 1/3 of the leading charts, and the Silverlight Toolkit charts come last.

You can download the full sourcecode of this comparison here: ChartPerformance.zip

NOTE: To build the examples you need to download the charting components from their respective web pages:

Finally, a couple of my colleagues have created similar charting tests using other technologies, you might be interested in Graham’s Flex implementation, or Chris’s cute HTML5 implementation.

Regards, Colin E.

Visiblox Charts vs. Silverlight Toolkit Charts – a test of Performance

November 8th, 2010

This blog post compares the performance of the Visiblox and Silverlight Toolkit charts using a simple image processing tool to test and illustrate their differences in performance. The results show that the Visiblox charts are approximately 50 – 100 times faster that the Silverlight Toolkit charts.

UPDATE: I have published a more up-to-date and extensive test of WPF charting components in a more recent blog post. This new test looks at how raster-graphics can be used to significantly enhance performance.

I was recently asked by my friends at Visiblox to test out the latest release of their charts for WPF and Silverlight. One of the key features of the Visiblox charts is their performance; they have been designed to both render and update rapidly in response to changes in the underlying data. I decided to put their latest release to the test in a head-to-head against the Silverlight Toolkit charts.

Microsoft announced the release of the Silverlight Toolkit charts in late 2008 and have since released a number of updates, some of which have focussed on performance. I would anticipate that the relatively mature Silverlight Toolkit charts are probably most developer’s first choice when they need to display data graphically within their application.

Before we get into the details of my investigation, here are the two applications working side-by-side, one using the Visiblox chart and the other using the Toolkit charts. Click and drag a line across the image to plot the RGB pixel intensities along the line in the chart above. This is a reasonably tough challenge for the chart, with three series rendered of up to 400 points each. From the examples below you can see that the Visiblox charts are clearly faster.

Visiblox Chart Silverlight Toolkit Chart

[Hare image used under CC licence from flickr, tortoise used royalty free from stock.xchng]

I have had quite a bit of experience with the Silverlight Toolkit in the past and have written a few blog posts which detail how to add interactivity to the charts. The early releases were pretty slow, due to the large number of ‘Control’ instances that the chart constructs, (there are un-official guidelines that you should limit the number of control instances to a few hundred for WPF & Silverlight). However, subsequent releases were quite a bit faster. David Anson also published a number of very useful hints and tips on maximising chart performance on his blog. I have followed all of David’s guidelines to provide a fair comparison.

The following sections will provide a little more detail regarding the implementation of this application and how it differs between the Visiblox and Toolkit versions.

Visiblox Charts

The markup for the above application is quite simple:

<StackPanel Orientation="Vertical" x:Name="LayoutRoot">
  <Grid Height="180">
 
    <chart:Chart x:Name="chart"
                LegendVisibility="Collapsed"
                Margin="5,0,5,5">
      <chart:Chart.XAxis>
        <!-- a 'hidden' X axis -->
        <chart:LinearAxis ShowAxis="False"
                          GridlineStyle="{StaticResource GridLineStyle}"
                          IsMarginEnabled="False"/>
      </chart:Chart.XAxis>
      <chart:Chart.YAxis>
        <!-- A Y axis with labels within the chart area -->
        <chart:LinearAxis ShowLabels="True"
                          LabelsPosition="Inside"
                          GridlineStyle="{StaticResource GridLineStyle}"
                          ShowMajorTicks="False"
                          ShowMinorTicks="False"/>
      </chart:Chart.YAxis>
      <chart:Chart.Series>
        <!-- the series used to render the RGB components -->
        <chart:LineSeries LineStroke="#A00"/>
        <chart:LineSeries LineStroke="#0A0"/>
        <chart:LineSeries LineStroke="#00A"/>
      </chart:Chart.Series>
    </chart:Chart>
    <TextBlock x:Name="instructions"
                Text="Use the mouse to 'drag' a line across the image below ..."
                FontSize="10" Foreground="#333333" Margin="50" TextWrapping="Wrap"/>
  </Grid>
 
  <Grid x:Name="grid"
        MouseLeftButtonUp="Grid_MouseLeftButtonUp"
        MouseLeftButtonDown="Grid_MouseLeftButtonDown">
    <Image Width="300" Height="200" x:Name="image"               
                    Source="/hare.jpg"/>
    <Line Stroke="Black" StrokeThickness="3"
                  x:Name="line"/>
  </Grid>
 
</StackPanel>

The above markup constructs a Chart, Image and a Line within a StackPanel. The code-behind loads the image into a WriteableBitmap in order to access its pixel values, and the mouse event handlers attached to the grid are used to detect when the user drags the line. The technique I published previously on throttling mouse events is used to handle the mouse move event in order to construct the chart data:

private void ThrottledEvent_ThrottledMouseMove(object sender, MouseEventArgs e)
{
  if (!lButtonDown)
    return;
 
  line.X2 = e.GetPosition(grid).X;
  line.Y2 = e.GetPosition(grid).Y;
 
  // compute distance between the points
  double distance = Math.Sqrt((line.X1 - line.X2) * (line.X1 - line.X2) +
      (line.Y1 - line.Y2) * (line.Y1 - line.Y2));
 
  // create the Visiblox dataseries for the R, G & B components
  var dataR = new DataSeries<double, double>("R");
  var dataG = new DataSeries<double, double>("G");
  var dataB = new DataSeries<double, double>("B");
 
  // build the charts
  for (double pt = 0; pt < distance; pt++)
  {
    double xPos = line.X1 + (line.X2 - line.X1) * pt / distance;
    double yPos = line.Y1 + (line.Y2 - line.Y1) * pt / distance;
 
    int xIndex = (int)xPos;
    int yIndex = (int)yPos;
 
    int pixel = pixelData[xIndex + yIndex * 300];
 
    // the RGB values are 'packed' into an int, here we unpack them
    byte B = (byte)(pixel & 0xFF);
    pixel >>= 8;
    byte G = (byte)(pixel & 0xFF);
    pixel >>= 8;
    byte R = (byte)(pixel & 0xFF);
    pixel >>= 8;
 
    // add each datapoint
    dataR.Add(new DataPoint<double, double>(pt, R));
    dataG.Add(new DataPoint<double, double>(pt, G));
    dataB.Add(new DataPoint<double, double>(pt, B));
  }
 
  // set the data for each series
  chart.Series[0].DataSeries = dataR;
  chart.Series[1].DataSeries = dataG;
  chart.Series[2].DataSeries = dataB;
}

The Visiblox series (which implement IChartSeries) require date to be presented as an IDataSeries, which is a collection of IDataPoint instances. In the above code the chart data is copied directly into a DataSeries. Databinding is also possible via BindableDataSeries, however, for maximum performance it is always better to use DataSeries and copy the data across directly.

Silverlight Toolkit Charts

The Toolkit version of the application is much the same as the Visiblox one. The only difference in the application markup is for the chart itself, as shown below:

<ControlTemplate x:Key="SimplifiedDataPoint" TargetType="tk:LineDataPoint">
</ControlTemplate>
...
<tk:Chart x:Name="chart"
          LegendStyle="{StaticResource CollapsedLegendStyle}"
          TitleStyle="{StaticResource CollapsedStyle}">
 
  <!-- define the line series -->
  <tk:LineSeries ItemsSource="{Binding}"   
                  TransitionDuration="0"
                  DependentValueBinding="{Binding Intensity}" 
                  IndependentValueBinding="{Binding Location}">
    <tk:LineSeries.DataPointStyle>
      <Style TargetType="tk:LineDataPoint">
        <Setter Property="Visibility" Value="Collapsed"/>
        <Setter Property="Template" Value="{StaticResource SimplifiedDataPoint}"/>
        <Setter Property="Background" Value="#A00"/>
      </Style>
    </tk:LineSeries.DataPointStyle>
  </tk:LineSeries>
 
  <tk:LineSeries ItemsSource="{Binding}"   
                  TransitionDuration="0"
                  DependentValueBinding="{Binding Intensity}" 
                  IndependentValueBinding="{Binding Location}">
    <tk:LineSeries.DataPointStyle>
      <Style TargetType="tk:LineDataPoint">
        <Setter Property="Visibility" Value="Collapsed"/>
        <Setter Property="Template" Value="{StaticResource SimplifiedDataPoint}"/>
        <Setter Property="Background" Value="#0A0"/>
      </Style>
    </tk:LineSeries.DataPointStyle>
  </tk:LineSeries>
 
  <tk:LineSeries ItemsSource="{Binding}"   
                  TransitionDuration="0"
                  DependentValueBinding="{Binding Intensity}" 
                  IndependentValueBinding="{Binding Location}">
    <tk:LineSeries.DataPointStyle>
      <Style TargetType="tk:LineDataPoint">
        <Setter Property="Visibility" Value="Collapsed"/>
        <Setter Property="Template" Value="{StaticResource SimplifiedDataPoint}"/>
        <Setter Property="Background" Value="#00A"/>
      </Style>
    </tk:LineSeries.DataPointStyle>
  </tk:LineSeries>
 
  <!-- configure the axes -->
  <tk:Chart.Axes>
    <tk:LinearAxis Orientation="X" Height="0">
    </tk:LinearAxis>
  </tk:Chart.Axes>
</tk:Chart>

The markup here is a little more complex, in part because I am following the advice given by David Anson in his charting performance blog post in order to maximise the chart performance. With reference to David’s post, the following tips have been implemented:

  1. Turn off the fade in/out VSM animations – the template used by the DataPoint in my example has no visual states, hence will not be animated
  2. Change to a simpler DataPoint Template – the template in the example is as simple as possible, it is empty!
  3. Add the points more efficiently – the ItemsSource for each series are being updated via an ‘atomic’ re-assignment.
  4. Disable the data change animations – the TransitionDuration property has been set to zero as per David’s recommendation.

The other three tips in the blog post were not relevant to this test scenario.

The code-behind for the Toolkit chart version of this application is again much the same as for the Visiblox charts, the main difference is due to the Toolkit chart binding to business objects rather than having its own series type. Here is the implementation of the (throttled) mouse move event handler, and the business object which is bound to the chart.

/// <summary>
/// Handles mouse move to draw the line and intensity histograms
/// </summary>
private void ThrottledEvent_ThrottledMouseMove(object sender, MouseEventArgs e)
{
  if (!lButtonDown)
    return;
 
  line.X2 = e.GetPosition(grid).X;
  line.Y2 = e.GetPosition(grid).Y;
 
  // compute distance between the points
  double distance = Math.Sqrt((line.X1 - line.X2) * (line.X1 - line.X2) +
      (line.Y1 - line.Y2) * (line.Y1 - line.Y2));
 
  // create the series for the R, G &amp; B components
  var dataR = new List<DataPoint>();
  var dataG = new List<DataPoint>();
  var dataB = new List<DataPoint>();
 
  // build the charts
  for (double pt = 0; pt < distance; pt++)
  {
    double xPos = line.X1 + (line.X2 - line.X1) * pt / distance;
    double yPos = line.Y1 + (line.Y2 - line.Y1) * pt / distance;
 
    int xIndex = (int)xPos;
    int yIndex = (int)yPos;
 
    int pixel = pixelData[xIndex + yIndex * 300];
 
    // the RGB values are 'packed' into an int, here we unpack them
    byte B = (byte)(pixel & 0xFF);
    pixel >>= 8;
    byte G = (byte)(pixel & 0xFF);
    pixel >>= 8;
    byte R = (byte)(pixel & 0xFF);
    pixel >>= 8;
 
    // add each datapoint to the series
    dataR.Add(new DataPoint(pt, R));
    dataG.Add(new DataPoint(pt, G));
    dataB.Add(new DataPoint(pt, B));
  }
 
  // add each series to the chart
  ((LineSeries)chart.Series[0]).ItemsSource = dataR;
  ((LineSeries)chart.Series[1]).ItemsSource = dataG;
  ((LineSeries)chart.Series[2]).ItemsSource = dataB;
}
 
...
 
/// <summary>
/// A value object used to present the data to the chart
/// </summary>
public class DataPoint
{
  public double Location { get; private set; }
  public double Intensity { get; private set; }
 
  public DataPoint(double location, double intensity)
  {
    Location = location;
    Intensity = intensity;
  }
}

Conclusions

From the above example application we can see that the Visiblox charts performs approximately 50 – 100 times faster than the Silverlight Toolkit charts. This is of course a limited test that focuses on a specific mode of operation, however, the Visiblox charts have been designed with a focus on performance, so I would expect a similar result in other performance test cases.

The difference in speed between the two charting components can be broadly attributed to three key differences in design:

  1. Lightweight visuals – as mentioned in the introduction, the Toolkit charts are ‘control’ heavy, pushing the limits of the framework guidelines, in order to provide flexible styling and templating. The Visiblox charts use a much more lightweight approach, however, if templating is required a TemplatedLineSeries can be used.
  2. Databinding as an option – with Visiblox it is possible to present the data directly to the chart using the DataSeries class, whilst with the Toolkit charts you have to use databinding which brings with it a performance overhead. Again, with Visiblox you can use databinding to business objects if you wish. With the above Visiblox example application the performance is approximately halved by using databinding, this is still 25-50 times faster than the toolkit charts! (The sourcecode download includes the databinding Visiblox vesion).
  3. Optimised handling of changes – the Visiblox charts are optimised in such a way as to minimise the impact of changes in the data or other properties of the chart. You can update datapoints, entire series, or change the various properties of an axis and be guaranteed that the chart will only re-computed its layout once.

You can download the full sourcecode for the example applications given in this blog post here: ChartPerformance.zip

For the Visiblox examples you will need to visit www.visiblox.com to download their chart component, and for the Toolkit examples you will need to download and install the Silverlight Toolkit.

Regards, Colin E.