Showing posts with label graphic. Show all posts
Showing posts with label graphic. Show all posts

Friday, 13 April 2012

C# performance counters impact performance? No, but it seems that Chart.SaveImage does.

A little thought later and it looks like no, they don’t impact performance. What I was seeing was the effect of prepping and drawing a chart – on about the same timescale that I was querying for information.

Diagnosing the problem was straightforward; I crosschecked the processor usage for the tool with perfmon. After configuring it to avoid saving down a chart the processor usage dropped to zero.

It is a good example of misleading yourself while looking at performance stats. I could see usage spiking on a period that looked suspicious. I used the VS 2010 in-built profiler and saw that PerformanceCounter.NextValue() was by far the biggest contributor to CPU time, and put two and two together to make five.

I ran the app for a few minutes, rather than the one minute I originally tested with, and the stats inverted. Chart.SaveImage() was overwhelmingly responsible for CPU consumption, with the calling method, Agent.Graph(), taking up ~71% of samples.

Schoolboy misinterpretation, but as always a bit of thought and investigation soon shows up the problem.

Now. Why is Chart.SaveImage() so expensive, and is there a cheaper alternative?

Wednesday, 11 April 2012

Creating a chart programmatically in C# using DataVisualization.Charting

Means you have to reference System.Windows.Forms and System.Windows.Forms.DataVisualization, even though you might be doing this in a console application. That said, it also includes a set of data manipulation functions, some of which are even finance-specific:

using System;
using System.Drawing;
using System.Windows.Forms.DataVisualization.Charting;

namespace Sandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            // set up some data
            var xvals = new[]
                {
                    new DateTime(2012, 4, 4), 
                    new DateTime(2012, 4, 5), 
                    new DateTime(2012, 4, 6), 
                    new DateTime(2012, 4, 7)
                };
            var yvals = new[] { 1,3,7,12 };

            // create the chart
            var chart = new Chart();
            chart.Size = new Size(600, 250);

            var chartArea = new ChartArea();
            chartArea.AxisX.LabelStyle.Format = "dd/MMM\nhh:mm";
            chartArea.AxisX.MajorGrid.LineColor = Color.LightGray;
            chartArea.AxisY.MajorGrid.LineColor = Color.LightGray;
            chartArea.AxisX.LabelStyle.Font = new Font("Consolas", 8);
            chartArea.AxisY.LabelStyle.Font = new Font("Consolas", 8);
            chart.ChartAreas.Add(chartArea);

            var series = new Series();
            series.Name = "Series1";
            series.ChartType = SeriesChartType.FastLine;
            series.XValueType = ChartValueType.DateTime;
            chart.Series.Add(series);

            // bind the datapoints
            chart.Series["Series1"].Points.DataBindXY(xvals, yvals);

            // copy the series and manipulate the copy
            chart.DataManipulator.CopySeriesValues("Series1", "Series2");
            chart.DataManipulator.FinancialFormula(
                FinancialFormula.WeightedMovingAverage, 
                "Series2"
            );
            chart.Series["Series2"].ChartType = SeriesChartType.FastLine;

            // draw!
            chart.Invalidate();

            // write out a file
            chart.SaveImage("chart.png", ChartImageFormat.Png);
        }
    }
}

Gives the following result:

chart

Thursday, 17 May 2007

double buffering in C#

Don't do anything other than copy a back buffer to the control in the controls paint method.

Don't necessarily call Refresh() from event handlers that modify the image -- it's inefficient to call Refresh from a mouse movement handler that handles dragging of an object across the control, for example. Draw on a back buffer then Refresh() at set points in time (<= 250 ms) using a System.Windows.Forms.Timer.

Code snippets here as a reminder:

private System.Windows.Forms.Timer timer;
private BufferedGraphicsContext context;
private BufferedGraphics gfx;
private System.Windows.Forms.Panel panel1;

public Form()
{
this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1Paint);

this.SetStyle(ControlStyles.AllPaintingInWmPaint  ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

context = BufferedGraphicsManager.Current;
context.MaximumBuffer = new Size(this.panel1.Width + 1, this.panel1.Height + 1);

gfx = context.Allocate(this.panel1.CreateGraphics(),
new Rectangle(0, 0, this.panel1.Width, this.panel1.Height));

timer = new System.Windows.Forms.Timer();
timer.Interval = 100;
timer.Tick += new EventHandler(this.OnTimer);
timer.Start();
}

private void OnTimer(object sender, EventArgs e)
{
DrawToBuffer(gfx.Graphics);
gfx.Render(Graphics.FromHwnd(this.panel1.Handle));
}

void panel1Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
gfx.Render();
}

private void panel1Resize(object sender, EventArgs e)
{
context.MaximumBuffer = new Size(this.panel1.Width + 1, this.panel1.Height + 1);
if (gfx != null)
{
gfx.Dispose();
gfx = null;
}
gfx = context.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.panel1.Width, this.panel1.Height));
DrawToBuffer(gfx.Graphics);
this.Refresh();
}