Bottleneck

Cags

Contributor
Joined
Feb 19, 2004
Messages
695
Location
Melton Mowbray, England
I've written a small section of code that takes alot longer to complete than I think it should. The code is actually using a Graphics object but I put this post in the General forum as I believe the bottleneck has nothing todo with the actuall drawing.

The following code takes approximately 480ms to complete (whereas simply drawing the equivalent number of rectangles takes about 9ms).

C#:
foreach(Square sqTemp in arrGrid)
{
	if(sqTemp.Type == SquareType.Node)
		gBuffer.FillRectangle(Brushes.Black, sqTemp.Bounds);
	else if(sqTemp.Type == SquareType.Link)
	{
		if(sqTemp.Link == LinkType.Yes)
		{
			gBuffer.FillRectangle(Brushes.Black, sqTemp.Bounds);
		}
		else if(sqTemp.Link == LinkType.No)
		{
			gBuffer.FillRectangle(Brushes.Red, sqTemp.Bounds);
		}
	}
}

Given....

C#:
Square[,] arrGrid = new Square[31,51];

public enum SquareType
{
	Node, Link, Number
}

public enum LinkType
{
	Unknown, Yes, No
}

Class square is a small class with properties for Bounds, LinkType and SquareType. I was originally using a nested for loop to cycle through each square, but found that using a foreach loop was slightly quicker. I'm wondering if using Square[,] is not a good method of creating an array, maybe I should build a custom collection by inheriting collectionbase.

Has anybody got any ideas on the best way of speeding up this method? (as my application is relatively simple I can always get around it by drawing only parts of it at a time, I just thought I'd check to see if anyone notices an obvious bottleneck first)
 
You are drawing 1581 rectangles, that is kinda alot. I don't know if there are any differences between the rectangles you drew in 9 ms and the ones you draw in 500. These are a few things that might speed stuff up a bit:

-Make sure anti-aliasing is set to SmoothingMode.None for the graphics object
-Try using a for loop instead of a foreach loop. I'm guessing that the for loop will be faster. I don't think it will help that much though.
-Try using a jagged array. In .Net 1.x, the clr is optimized for one dimensional arrays, and jagged arrays are simply one-dimensional arrays of one-dimensional arrays. (Keep in mind that jagged arrays aren't CLS compliant so they shouldn't be publicly accessible)

I don't know how much those suggestions will really help though.
C#:
Square[][] arrGrid = new Square[31][];

void MakeSquareArray() {
    for(int i = 0; i < 31; i++) {Square[i] = new Square[51];}
}
 
public enum SquareType
{
	Node, Link, Number
}
 
public enum LinkType
{
	Unknown, Yes, No
}
C#:
for(int i = 0; i < 31; i++) {
    for(int j = 0; j < 51; j++) {
		Square sqTemp = Square[i][j];
		if(sqTemp.Type == SquareType.Node)
			gBuffer.FillRectangle(Brushes.Black, sqTemp.Bounds);
		else if(sqTemp.Type == SquareType.Link)
		{
			if(sqTemp.Link == LinkType.Yes)
			{
				gBuffer.FillRectangle(Brushes.Black, sqTemp.Bounds);
			}
			else if(sqTemp.Link == LinkType.No)
			{
				gBuffer.FillRectangle(Brushes.Red, sqTemp.Bounds);
			}
		}
	}
}
Also, I haven't tested this code.
 
I tested drawing 1581 rectangles with code along the lines of

C#:
Rectangle rectBounds = new Rectangle(0,0, 5, 5); // same size as those in the array;

for(int i = 0; i < 1581; i++)
{
gBuffer.DrawRectangle(Brushes.Black, rectBounds);
}

This code actually draws 1581 rectangles and it is worth noting that using my original code 375 squares are nodes, and these are the only ones actually being drawn. The squares of SquareType Link are all LinkType Unknown. This is why I came to the conclusion that the DrawRectangle is not the line of code causing significant slowdown.

With regards to the for being quicker than a foreach, I have tried both and found foreach slightly quicker (this was probably due to referencing the array item multiple times, rather than a local object which is created with a for each loop).
 
There is a big difference between DrawRectangle and FillRectangle. Unless that's a typo, that would explain a little.
 
Maybe we could see the whole project?

Here's a sample I used that should mimic what you have. For me, running the one big loop or using what I think is your same comparison code, I get the same performance. I chose to do the drawing in OnPaint - maybe you did your graphics elsewhere?

Just paste this into a new Form1 (replace everything) of a Windows C# project.

C#:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;

namespace DotNetForumsTest1
{
	public enum SquareType
	{
		Node, Link, Number
	}
 
	public enum LinkType
	{
		Unknown, Yes, No
	}

	internal class Square
	{
		public SquareType Type;
		public LinkType Link;
		public Rectangle Bounds;

	}

	public class Form1 : System.Windows.Forms.Form
	{
		Square[][] arrGrid = new Square[31][];
 
		public Form1()
		{
			InitializeComponent();

			for(int i = 0; i < arrGrid.Length; i++) 
			{
				arrGrid[i] = new Square[51];
				for(int j=0; j<arrGrid[i].Length; j++)
				{
					arrGrid[i][j] = new Square();
					arrGrid[i][j].Type = SquareType.Node;
					arrGrid[i][j].Link = LinkType.Unknown;
					arrGrid[i][j].Bounds = new Rectangle(0, 0, 5, 5);
				}
			}
		}

		private void InitializeComponent()
		{
			this.Size = new System.Drawing.Size(300,300);
			this.Text = "Form1";
		}

		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			int last = Environment.TickCount;
//			Rectangle rectBounds = new Rectangle(0,0, 5, 5); // same size as those in the array;
//			for(int i = 0; i < 1581; i++)
//			{
//				e.Graphics.FillRectangle(Brushes.Black, rectBounds);
//			}

			for(int i = 0; i < arrGrid.Length; i++) 
			{
				for(int j=0; j<arrGrid[i].Length; j++)
				{
					e.Graphics.FillRectangle(Brushes.Black, arrGrid[i][j].Bounds);
				}
			}

			Debug.WriteLine((Environment.TickCount - last).ToString());
		}
	}
}

-ner
 
Thanks for your help I've worked out what was causing the slowdown, I was using the wrong variable when creating the array, making it 40 * wider and 40 * heigher than it should have been (hence an attempt to draw a hell of alot more rectangles than I thought it was drawing).

On a side note Nerseus thats a reasonable approximation of my application the only real differences are that I'm using a [,] array currently not [][] and also I'm doing the drawing in a seperate method (to a bitmap which is painted to the screen in the OnPaint event).
 
Back
Top