Jump to content

 

Photo

Performance hog


  • Please log in to reply
28 replies to this topic

#1 Nathan Brouwer

Nathan Brouwer

    Advanced Member

  • Members
  • PipPipPip
  • 74 posts

Posted 13 April 2009 - 07:45 AM

Hiya guys,

since the last version Im noticing some considerable performance issues with the rendering of the charts. Especially, since the last beta version it takes a couple of seconds to draw one chart on the screen.

Im not talking about 1000 datapoints here, even 50 datapoints seems to be causing performance issues.

Is this a known issue ? Are you planning any performances updates soon?

Let me know please, its slowly becoming unworkable...

Regards, Nathan

Ps. Let me know if you need a code sample of our charts... Or an xml dump?

#2 sunil

sunil

    Advanced Member

  • Banned
  • PipPipPip
  • 239 posts
  • Gender:Male

Posted 13 April 2009 - 07:53 AM

Hi Nathan,

We have come across performance issues when there are more than 1000 DataPoints. We are currently working on improving the performance.

It would be helpful if you can provide us with Sample Code.
Regards,
Sunil Urs
Team Visifire
Webyog

#3 Nathan Brouwer

Nathan Brouwer

    Advanced Member

  • Members
  • PipPipPip
  • 74 posts

Posted 14 April 2009 - 09:22 AM

Hi Sunil,

actually, we're talking about a maximum of 50 series with a maximum of 2 datapoints in each serie. We actually notice a considerable slowdown when the browser is trying to draw the charts, at some points it completely locks it (for maybe 2 seconds). Also, handling the onClick events on each datapoint takes a while to bubble through.

All of this becamse worse when we started using the beta releases. Now, Im going to to try to revert to the 2.1.0 release and hope that thats better..

Regards, Nathan

#4 vivek

vivek

    Advanced Member

  • Team Visifire
  • PipPipPip
  • 3,605 posts
  • Gender:Male
  • Location:Bangalore

Posted 14 April 2009 - 03:24 PM

Hi Nathan,


We just tested with the case you mentioned. But it was just taking 900 - 1100 milliseconds to render the chart. Below is the code.



public partial class Page : UserControl
{
public Page()
{
InitializeComponent();

CreateChart();
}

private void CreateChart()
{
dt1 = DateTime.UtcNow;

chart = new Chart();
chart.Width = 500;
chart.Height = 300;

DataSeries ds;

for (Int32 j = 0; j < 50; j++)
{
ds = new DataSeries();
ds.RenderAs = RenderAs.Column;

for (Int32 i = 0; i < 1; i++)
{
DataPoint dp = new DataPoint();
dp.XValue = i + 1;
dp.YValue = rand.Next(10, 100);
ds.DataPoints.Add(dp);
}

chart.Series.Add(ds);
}

chart.Rendered += new EventHandler(chart_Rendered);

LayoutRoot.Children.Add(chart);
}

void chart_Rendered(object sender, EventArgs e)
{
dt2 = DateTime.UtcNow;
Double time = (dt2 - dt1).TotalMilliseconds;
System.Diagnostics.Debug.WriteLine("Time " + time);
}

Chart chart;
Random rand = new Random();
DateTime dt1;
DateTime dt2;
}



Can you please provide us with the code which is taking more time.

Regards,
Vivek
Team Visifire


#5 vivek

vivek

    Advanced Member

  • Team Visifire
  • PipPipPip
  • 3,605 posts
  • Gender:Male
  • Location:Bangalore

Posted 15 April 2009 - 04:37 AM

Hi Nathan,

We have optimized the performance while rendering and clearing the chart from a UIElement. We will try to optimize it further.
Please check with an internal release build here. Also please check with your existing test cases and let us know if any problem occurs.

Regards,
Vivek
Team Visifire


#6 Nathan Brouwer

Nathan Brouwer

    Advanced Member

  • Members
  • PipPipPip
  • 74 posts

Posted 15 April 2009 - 05:15 AM

Hi Vivek,

thanks for your help. I was wondering, could it be that performance is affected by adding a click handler on every datapoint? Also, we tried reverting back to the 2.1.0 version and performance is really much better! Where in the beta 4 version the browser seemed non responsive while rendering, we dont get that effect anymore when using the 2.1.0 version...

Hope that helps? Anyway, I'll just test the build you did! Thanks for helping me! Appreciate it.

Regards, Nathan

#7 vivek

vivek

    Advanced Member

  • Team Visifire
  • PipPipPip
  • 3,605 posts
  • Gender:Male
  • Location:Bangalore

Posted 15 April 2009 - 02:16 PM

Hi Nathan,

We tested with the MouseEvents on DataPoints. It just take 900 - 1200 milliseconds for rendering the chart.

Below is the code:

public partial class Page : UserControl
{
public Page()
{
InitializeComponent();

dt1 = DateTime.UtcNow;

CreateChart();
}

private void CreateChart()
{
chart = new Chart();
chart.Width = 600;
chart.Height = 300;

DataSeries ds;

for (Int32 j = 0; j < 50; j++)
{
ds = new DataSeries();
ds.RenderAs = RenderAs.Column;

for (Int32 i = 0; i < 2; i++)
{
DataPoint dp = new DataPoint();
dp.XValue = i + 1;
dp.YValue = rand.Next(10, 100);
dp.MouseLeftButtonDown += new MouseButtonEventHandler(dp_MouseLeftButtonDown);
ds.DataPoints.Add(dp);
}

chart.Series.Add(ds);
}

chart.Rendered += new EventHandler(chart_Rendered);

LayoutRoot.Children.Add(chart);
}

void dp_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
(sender as DataPoint).Color = new SolidColorBrush(Colors.Cyan);
}

void chart_Rendered(object sender, EventArgs e)
{
dt2 = DateTime.UtcNow;
Double time = (dt2 - dt1).TotalMilliseconds;
System.Diagnostics.Debug.WriteLine("Time " + time);
}

Chart chart;
Random rand = new Random();
DateTime dt1;
DateTime dt2;
}

Please provide us your sample code which is taking more time so that we can find the problem. Also let us know the performance feedback for the internal release build.

Regards,
Vivek
Team Visifire


#8 Nathan Brouwer

Nathan Brouwer

    Advanced Member

  • Members
  • PipPipPip
  • 74 posts

Posted 16 April 2009 - 02:41 AM

Hi Vivek,

Im working on extracting some code for you. Since the chart building is part of larger solution its hard to actually get some decent code sample. Anyway, here's some code, I hope it makes sense to you ... :rolleyes: Know that Im now going to try your internal build.

public static event OnDataPointClick DataPointClick;
		public static Service.Chart _chart = null;

		public static void CreateChart(Service.Chart data, Grid grid, ChartType chartType, Service.ChartType? chartAppearance)
		{
			Service.Chart chart = data as Service.Chart;
			_chart = data;
			
			grid.Children.Clear();

			//create chart
			Visifire.Charts.Chart vchart = new Visifire.Charts.Chart()
			{
				BorderThickness = new System.Windows.Thickness(0),
				AnimationEnabled = false,
				Watermark = false,
				View3D = false,
				ToolTipEnabled = true,
			};

			Style style = App.Current.Resources["ChartStyle"] as Style;
			vchart.Style = style;
			vchart.HorizontalAlignment = HorizontalAlignment.Stretch;
			vchart.VerticalAlignment = VerticalAlignment.Stretch;
			vchart.ScrollingEnabled = false;

			SolidColorBrush chartLabelColor = new SolidColorBrush(Color.FromArgb(255, 153, 153, 153));
			SolidColorBrush axisColor = new SolidColorBrush(Color.FromArgb(255, 192, 192, 192));

			switch (chartType)
			{
				case ChartType.ChartX:
					vchart.AxesX.Add(CreateXAxis());
					vchart.AxesY.Add(CreateYAxis(axisColor, chartLabelColor));
					break;
				case ChartType.ChartY:
					vchart.AxesY.Add(CreateYAxis(axisColor, chartLabelColor));
					break;
				case ChartType.ChartXY:
					vchart.AxesX.Add(CreateXAxis());
					vchart.AxesY.Add(CreateYAxis(axisColor, chartLabelColor));
					break;
				case ChartType.ChartXYLegend:
					break;
			}

			foreach (var serie in chart.ChartSeries)
			{
				Visifire.Charts.DataSeries vserie = new Visifire.Charts.DataSeries();
				if (chartAppearance != null)
					vserie.RenderAs = MapRenderAs((Service.ChartType)chartAppearance);
				
				//vserie.XValueType = ChartValueTypes.Date;
				vserie.XValueFormatString = "#0.#";

				foreach (var point in serie.ChartPoints)
				{
					Visifire.Charts.DataPoint dp =new Visifire.Charts.DataPoint()
					{
						Color = new SolidColorBrush(AppHelper.ColorHelper.ToColor("#eebe2f")),
						YValue =  point.YValue,
						ToolTipText = point.XValue + ":" + string.Format("{0:0,0}", point.YValue),
						XValue = point.XValue
					};
				   
					if (vserie.RenderAs == RenderAs.Line)
						dp.MarkerSize = 2;   

					vserie.DataPoints.Add(dp);
				}
				vchart.Series.Add(vserie);
				foreach (var dataPoint in vserie.DataPoints)
				{
					dataPoint.MouseEnter += new EventHandler<MouseEventArgs>(dataPoint_MouseEnter);
					dataPoint.MouseLeave += new EventHandler<MouseEventArgs>(dataPoint_MouseLeave);
					dataPoint.MouseLeftButtonDown += new MouseButtonEventHandler(dataPoint_MouseLeftButtonDown);
				}
			}
			grid.Children.Add(vchart);
		}

		internal static void CreateChart(Service.Chart data, Grid grid, ChartType chartType)
		{
			CreateChart(data, grid, chartType, null);
		}

		internal static Axis CreateXAxis()
		{
			return new Axis() { 
				ValueFormatString = "#0.#", 
				Enabled = false, 
				AxisLabels = new AxisLabels() 
				{ Enabled = false, } 
			};
		}

		internal static Axis CreateYAxis(SolidColorBrush axisColor, SolidColorBrush chartLabelColor)
		{
			double interval = Math.Ceiling((((_chart.MaxY + 1000) / 1000) * 1000) / 4);
			return new Axis()
			{
				Enabled = true,
				Interval = interval,
				LineColor = axisColor,
				StartFromZero = true,
				AxisLabels = new AxisLabels()
				{
					Enabled = true,
					FontColor = chartLabelColor,
				}
			};
		}

		internal static void dataPoint_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			DataPoint dp = sender as DataPoint;
			if (DataPointClick != null)
			{
				.YearlyOverview data = DataPointClick();
				MultipleYearDialog myd = new MultipleYearDialog(_chart, data, (int)(dp.XValue - DateTime.Now.Year));
				Dialog.Show(myd);
			}
		}

		internal static void dataPoint_MouseLeave(object sender, MouseEventArgs e)
		{
			Visifire.Charts.DataPoint dp = sender as Visifire.Charts.DataPoint;
			dp.Color = new SolidColorBrush(AppHelper.ColorHelper.ToColor("#eebe2f"));
		}

		internal static void dataPoint_MouseEnter(object sender, MouseEventArgs e)
		{
			Visifire.Charts.DataPoint dp = sender as Visifire.Charts.DataPoint;
			dp.Color = new SolidColorBrush(AppHelper.ColorHelper.ToColor("#646179"));
		}

		internal static RenderAs MapRenderAs(Service.ChartType chartAppearance)
		{
			switch (chartAppearance)
			{
				case Service.ChartType.StackedColumn:
				default:
					return RenderAs.StackedColumn;
				case Service.ChartType.Column:
					return RenderAs.Column;
				case Service.ChartType.Pie:
					return RenderAs.Pie;
				case Service.ChartType.Doughnut:
					return RenderAs.Doughnut;
				case Service.ChartType.Line:
					return RenderAs.Line;
				case Service.ChartType.Area:
					return RenderAs.Area;
				case Service.ChartType.Bar:
					return RenderAs.Bar;
				case Service.ChartType.StackedBar:
					return RenderAs.StackedBar;
				case Service.ChartType.StackedArea:
					return RenderAs.StackedArea;
				case Service.ChartType.Point:
					return RenderAs.Point;
				case Service.ChartType.Bubble:
					return RenderAs.Bubble;
			}
		}


#9 Nathan Brouwer

Nathan Brouwer

    Advanced Member

  • Members
  • PipPipPip
  • 74 posts

Posted 16 April 2009 - 03:23 AM

Hiya Vivek,

I can honestly say that in your last build, performance has improved significantly. We do have a problem with charts starting 10 years before our first datapoint (I verified, the first serie / datapoint I add to the collection is actually in 20090101, yet the chart starts at 1999). Also, it adds another few years at the end of the chart...

Please see here:
Posted Image

Also, when charts are small, they appear in a really strange matter:
Posted Image

Any thoughts about the above two items?

Regards, Nathan

#10 vivek

vivek

    Advanced Member

  • Team Visifire
  • PipPipPip
  • 3,605 posts
  • Gender:Male
  • Location:Bangalore

Posted 16 April 2009 - 07:53 AM

Hi Nathan,

Yes there are some issues in DateTime axis. Currently we are trying to improve the DateTime support.

Regarding your second query, you are setting Interval property in AxisY which is very less as compared to the minimum and maximum value of axis, because of which the AxisLabels are overlapping each other. I would suggest you to do either of the following things:

1) Don't set Interval property in AxisY so that Visifire will generate it automatically.

2) If you are setting Interval property then set some bigger value so that it will not generate AxisLabels as shown in the image.

Regards,
Vivek
Team Visifire


#11 Nathan Brouwer

Nathan Brouwer

    Advanced Member

  • Members
  • PipPipPip
  • 74 posts

Posted 16 April 2009 - 10:39 AM

Hi Vivek,

you're right. We were setting the interval to a positive value but the values in the chart were all negative. We skip the interval set when the MaxYValue is 0 (or less).

Regarding my code, is there anything you'd recommend? Though the new beta you published is faster than the previous, I still find it considerably slower than version 2.1.0...?

You're not noticing differences in chart buildup?

Regards, Nathan

#12 vivek

vivek

    Advanced Member

  • Team Visifire
  • PipPipPip
  • 3,605 posts
  • Gender:Male
  • Location:Bangalore

Posted 16 April 2009 - 03:09 PM

Hi Nathan,

I have checked your code with the internal release build (Visifire 2.2.0 beta6) and the Visfire 2.1.0 and calculated some benchmarking performance data while chart renders as shown below:

No of			 No of				 Visifire 2.2.0 beta6			Visifire 2.1.0
DataSeries	 DataPoints			 (In Milliseconds)			(In Milliseconds)
															

	50				   2					   1120 - 1150				   1032 - 1100   
	 1				  500					 1200 - 1300				   1240 - 1290
	 1				 1000					1650 - 1780				   2250 - 2280
	 2				 2000					4800 - 5300				 14800 - 15200

It seems with large number of DataPoints, the current internal release build is much faster than Visifire 2.1.0. Still we are trying to find out what and where is the problem. Could you please check with the same data and send us the benchmarking report.

Regards,
Vivek
Team Visifire


#13 thesolotraveler

thesolotraveler

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 17 April 2009 - 03:59 AM

Hi Vivek,
I figured out where is the performance bottle neck in v2.2.0, when using datetime values. if it can be of any help
specially that now the performance decreases alot not just in the rendering but in adding the datapoints.

A very bad coding technique is being used in event handler DataPoint.OnXValuePropertyChanged
which is relying on Exception being thrown for each point in a series, in order to set it as datatime.
then if you have 1000 point, u'll have 1000 exception and fall back to the catch statement.
taken this off, the speed went up from 35 seconds, to 1 second !!

Not sure if that's somthing you fixed in beta6

Cheers

#14 vivek

vivek

    Advanced Member

  • Team Visifire
  • PipPipPip
  • 3,605 posts
  • Gender:Male
  • Location:Bangalore

Posted 17 April 2009 - 06:13 AM

thesolotraveler,

Thanks for the tip. We'll look into it soon and get back to you.
In beta 6 we did some changes because of which the time taken to render had decreased to 1.6 Seconds for 1000 DataPoints. But what you suggested can reduce it to 1 sec, thats still a 37% boost to the performance. We'll surely find a better way to set DateTime.

Regards,
Vivek
Team Visifire


#15 Nathan Brouwer

Nathan Brouwer

    Advanced Member

  • Members
  • PipPipPip
  • 74 posts

Posted 18 April 2009 - 08:02 AM

Vivek,

is that added (thesolotraveler's remark) in the beta 7 release you just did ?

regards, Nathan

#16 sunil

sunil

    Advanced Member

  • Banned
  • PipPipPip
  • 239 posts
  • Gender:Male

Posted 18 April 2009 - 08:14 AM

Hi Nathan,

It has not been implemented yet. As far as I understand, what thesolotraveler has mentioned is that, if we remove the Numeric part ( which throws exception ), it improves the performance with DateTime Data Type. But it works fine for people who are giving DateTime always. We need to take care of the other side too - Numeric Data Type. We have not been able to work on that yet. Its in queue... :-)

We should be able to implement a decent solution in couple of weeks.

Regards,
Sunil Urs
Regards,
Sunil Urs
Team Visifire
Webyog

#17 thesolotraveler

thesolotraveler

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 20 April 2009 - 01:03 AM

Hi, to clarify my point,
you are doing this in DataPoint.OnXValuePropertyChanged

try
{
dataPoint.InternalXValue = Double.Parse(Convert.ToString(e.NewValue, System.Globalization.CultureInfo.InvariantCulture), System.Globalization.CultureInfo.InvariantCulture);
}
catch
{
dataPoint.InternalXValueAsDateTime = (e.NewValue.GetType().Equals(typeof(DateTime))) ? (DateTime)e.NewValue : DateTime.Parse(Convert.ToString(e.NewValue, ........................
}

So, if I have 1000 points with datetime values, then the Double.Parse in the try will fail with an exception 1000 times .

Thanks

#18 sunil

sunil

    Advanced Member

  • Banned
  • PipPipPip
  • 239 posts
  • Gender:Male

Posted 21 April 2009 - 07:40 AM

Hi thesolotraveler,

Now I was able to understand why it was taking 30 seconds on your system with those lines and not for me... :-)

Below are the few things that we need to consider
1. try-catch block is hit only if the user if setting the DataPoints through XAML. Else, their value will be set in the previous block itself
2. Those 1000 Exceptions are taking more time in Debug Mode ( if you run using F5 ) but not in the other case - "Start Without Debugging" (Ctrl-F5) - It was pretty quick without Debug.

Yesterday I had not tried by giving DataPoints in XAML + Debug.

So, even if they are taking longer in Debug mode, they ll not have much effect with normal applications performance wise - But I do agree that it is a bad practice... :-)

Now we tried changing the code as below uing Double.TryParse and DateTime.TryParse instead of try-catch and it worked pretty well!!

if (DateTime.TryParse((string)e.NewValue, out result))
				{
					dataPoint.InternalXValueAsDateTime = result;
					dataPoint.XValueType = ChartValueTypes.DateTime;
				}

Now it takes around one second even for the case of "Data in XAML + Debug"

Thank you very much for finding out the issue... It was very helpful.

Regards,
Sunil Urs
Regards,
Sunil Urs
Team Visifire
Webyog

#19 thesolotraveler

thesolotraveler

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 22 April 2009 - 12:22 AM

Hi Sunil,
Thanks.
I'm not setting my points through Xaml, I'm setting them through code. But you are right, without debug works fine,
but we developers live most our life in debug  mode :-)

It's good it's better now any way.

mmm, actually I don't think you need Double.TryParse, as you already check for type,  this is how I changed it

                if(String.IsNullOrEmpty(e.NewValue.ToString()))
                    dataPoint.InternalXValue = Double.NaN;

                else if(e.NewValue.GetType().Equals(typeof(Double)) || e.NewValue.GetType().Equals(typeof(Int32)))
                {
                    dataPoint.InternalXValue = Convert.ToDouble(e.NewValue, System.Globalization.CultureInfo.InvariantCulture);
                    dataPoint.XValueType = ChartValueTypes.Numeric;
                }

                else if(e.NewValue.GetType().Equals(typeof(DateTime)))
                {
                    dataPoint.InternalXValueAsDateTime = (e.NewValue.GetType().Equals(typeof(DateTime))) ? (DateTime)e.NewValue : DateTime.Parse(Convert.ToString(e.NewValue,                   System.Globalization.CultureInfo.InvariantCulture), System.Globalization.CultureInfo.InvariantCulture);
                    dataPoint.XValueType = ChartValueTypes.DateTime;
                }

But ofcourse, there is always more than one good way to do something :) , just thought I share what I did with you..

There are a two issues that I hope could be provided soon really :
- A fix to having an empty day our hour in the datetime X axis
- An accurate way, if converting a pixel X position on a canvas where the chart is, to a value on the x axis

Thanks

#20 thesolotraveler

thesolotraveler

    Advanced Member

  • Members
  • PipPipPip
  • 36 posts

Posted 22 April 2009 - 03:18 AM

might wanna read previous post first..

Hope I'm not making much out of this :)

But Actually, what I reall think is, you should always just Parse not TryParse, according to the XValueType of the series, in a try catch, hency only failing if supplied value is different from the type set to XValueType.

try
{
if(series.XValueType == ChartValueTypes.DateTime || series.XValueType == ChartValueTypes.Date || series.XValueType == ChartValueTypes.Time)
    DateTime.Parse();
else if(series.XValueType == ChartValueTypes.Double)
    Double.Parse();
}
catch
{
   maybe through exception, of invalid point value type                    (Indicate that values supplied are not of type specified for series)
}

Or even just a cast not parse if you no longer have to use a formatter.       Not sure if casting is faster than parsing though..
if(series.XValueType == Date)
   value = (DateTime) newValue;

Regards.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users