Groups | Blog | Home
all groups > dotnet performance > january 2004 >

dotnet performance : Optimising Lockbit Routine??


sam NO[at]SPAM samdavies.co.uk
1/26/2004 4:58:22 AM
Hi,

I'm having some problems getting the required speed for a small app
i'm currently writting. I rewrote the getPixel() fucntion that i
thought was my orginal bottle neck, but i'm still taking 5-6 minutes
to calcualte the score of around 6000 points. Does anyone have any
suggestions as to other problems that may be slowing down this
function??


private int returnArgb(GraphicsPath linePath, Bitmap Bmp)
{
int Argb;
int score = 0;
int RGBLightBlue = (Color.LightBlue.R<<16)+(Color.LightBlue.G<<8)+(Color.LightBlue.B);
int RGBWhite = (Color.White.R<<16)+(Color.White.G<<8)+(Color.White.B);
int RGBRed = (Color.Red.R<<16)+(Color.Red.G<<8)+(Color.Red.B);
int RGBDarkGreen = (Color.DarkSeaGreen.R<<16)+(Color.DarkSeaGreen.G<<8)+(Color.DarkSeaGreen.B);

paint.progressBar1.Maximum = linePath.PointCount;
paint.statusBar1.Text = "Color Detection";

BitmapData bmData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width,
Bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;

unsafe
{
int PixelSize = 3;
byte blue, green, red;
for(int i = 0; i<linePath.PointCount; i++)
{
paint.progressBar1.PerformStep();
int y = Convert.ToInt16(linePath.PathPoints[i].X);
int x = Convert.ToInt16(linePath.PathPoints[i].Y);

byte* row=(byte *)bmData.Scan0+(x*bmData.Stride);
blue = row[(y*PixelSize)];
green = row[(y*PixelSize)+1];
red = row[(y*PixelSize)+2];

Argb = (red<<16)+(green<<8)+(blue);


if(Argb == RGBLightBlue)
continue;
else if(Argb == RGBRed)
{
score = score+2;
}
else if(Argb == RGBWhite)
{
score = score+2;
}
else if(Argb == RGBDarkGreen)
{
score = score+4;
}
else
score++;
}
}

Bmp.UnlockBits(bmData);

return score;
}



Regards
Ray Beckett
1/26/2004 11:12:14 AM
A couple suggestions and a question...

First and most important, don't update the progress bar with every
iteration. GDI+ calls are very expensive performance wise. Try to
determine an appropriate interval to update the progress bar. Using a power
of 10 for your interval is usually ok (10, 1000, 10000), but experiment with
it to determine the best interval.

Next, try using a switch statement as opposed to 'if/if else/if else'.
There is another thread in this newsgroup that indicates switch produces
more efficient IL.

Third, I don't know if this will help performance or not, but the IL is a
little different (uses the dup opcode instead of ldarg a second time), but
you could use 'score+=2' instead of 'score=score+2'. I'm guessing the dup
opcode is more efficient due to already having the value on the stack and
just copying that value instead of having to process the value from the
variable a second time, but I don't know how much better

Fourth, instead of having the system calculate 'y*PixelSize' three times,
calculate it once and cache the value.
int y = Convert.ToInt16(linePath.PathPoints[i].X);
becomes
int y = (Convert.ToInt16(linePath.PathPoints[i].X))*PixelSize;

Fifth, if you expect the values from PathPoints to all be integers, don't
use the 'Convert' class functions. The class calls into mscorlib to do the
conversion, and that incurs more overhead. Change the line to
int y = ((short)(linePath.PathPoints[i].X))*PixelSize;
Now, the opcode conv.i4 is used. This is much faster than a call into
another assembly (add method to stack, etc...)




Question: Is there a reason you are converting the floating point value
from 'linePath.PathPoints[i].X(Y)' to a 16 bit integer as opposed to a 32
bit integer? The variables you are using for the values are 32 bit.
Without looking at the IL produced, this would seem to force a conversion
from float(single) to a 16 bit integer, then it has to be cast to a 32 bit
integer for storage in the variable.
If you don't need to do the conversion to a 16 bit integer (short) then the
line
int y = ((short)(linePath.PathPoints[i].X))*PixelSize;
changes to
int y = ((int)(linePath.PathPoints[i].X))*PixelSize;

Hope this helps. There are probably other minor optimizations, but these
are the ones I can see being helpful, especially the interval for the
progress bar update.

Ray

[quoted text, click to view]

sam NO[at]SPAM samdavies.co.uk
1/27/2004 3:31:47 AM
Hey Ray,

Thnaks for the help, was exactly what i was looking for. I'm afraid
the int conversion stuff you picked up on just highlighted my limited
programming experience. I made all the changes you suggested but
although i have a slight improvment in speed, it still taking a couple
of minutes to run though my graphics path

I read somewhere on another thread that the graphics path is a
particuarly slow way to access data and that i might be adviced to use
something quicker. I could if needed not write to a graphics path in
the first place and instead use an array or something similar. Do you
have any ideas?

Regards
Sam

[quoted text, click to view]
Tom Hall
1/27/2004 11:00:45 AM
This may be "way out there" but at the expense of memory, could you use the
format Format32bppRgb?

Then, you could pick up the data using one array access to grab all 4 bytes
(use an array of Ints) and eliminate all the shifting as now you could just
compare the Int you read with your precalculated Ints for the colours.

I believe the format is R,G,B,nothing so you'd have to shift your
precalculated colour Ints further to the left.

This is way I did this sort of thing in Assembler for the 68000 many years
ago!

Also, are you running this in Visual Studio because I believe all the
optimizations are turned OFF if you are!

Really you need to profile this and find out where the bottleneck is. Even
just outputing the current time before you do your graphics path things, and
before and after this routine will show you where to start.

Does this help?
Tom

[quoted text, click to view]

Michael Giagnocavo [MVP]
1/29/2004 1:07:43 PM
Good points,

[quoted text, click to view]

I wouldn't worry exceedingly about slightly different IL. In some cases,
this will result the exact same JIT output. Other suggestions look pretty
good :).

-mike
MVP


[quoted text, click to view]
Michael Giagnocavo [MVP]
1/29/2004 1:27:01 PM
Good points,

[quoted text, click to view]

I wouldn't worry exceedingly about slightly different IL. In some cases,
this will result the exact same JIT output.

[quoted text, click to view]

Using 32bit ints should be faster anyways. But, storing a 16bit int into a
32bit int doesnt have extra IL codes: conv.i2, stloc.x.

-mike
MVP

[quoted text, click to view]
Eric Gunnerson [MS]
2/2/2004 1:23:03 PM
A few comments:

1) Take a look at my FastBitmap class at:
http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=6699aa41-7b59-40ec-a30d-da3afe63bd05
2) If you can use pointer arithmetic rather than multiplication, things will
be much faster.
3) Try using a structure to be able to fetch out all values at once (see
PixelData class in above example).
4) Be very careful about using progress bars in tight loops. That could be a
big drain of resources. At the very least, try doing the update only every
10 or 100 steps.
5) Do your best to get rid of the convert calls


--
Eric Gunnerson

Visit the C# product team at http://www.csharp.net
Eric's blog is at http://weblogs.asp.net/ericgu/

This posting is provided "AS IS" with no warranties, and confers no rights.
[quoted text, click to view]

sam NO[at]SPAM samdavies.co.uk
2/3/2004 3:06:06 PM
hi Eric,

Just been having a play with your fastbit class and its running much
faster, i'm sure there are a few changes i can make to the function i
have going at the moment, but i'd like to remove a bug that i can't
seem to figure out.

The graphics path i am passing in hold the co-ordinates for lines that
either run vertically or horizotal across the screen. When a line is
loaded that is horizontal the software runs fine, however, when the
line is entered that is vertical at a certain point the loop throws an
error.

its obvious that somewhere i have not set up the correct widths and
height but i'm strugglering to fix the problem.

Any light you could shine on the mater would again be really
appriciated.

My function looks like this;
int Argb = 0;
int score = 0;
int RGBLightBlue =
(Color.LightBlue.R<<16)+(Color.LightBlue.G<<8)+(Color.LightBlue.B);
int RGBWhite = (Color.White.R<<16)+(Color.White.G<<8)+(Color.White.B);
int RGBRed = (Color.Red.R<<16)+(Color.Red.G<<8)+(Color.Red.B);
int RGBDarkGreen =
(Color.DarkSeaGreen.R<<16)+(Color.DarkSeaGreen.G<<8)+(Color.DarkSeaGreen.B);

unsafe
{
FastBitmap fastBitmap = new FastBitmap(Bmp);

fastBitmap.LockBitmap();

Point size = fastBitmap.Size;

for (int x = 0; x < linePath.PointCount; x++)
{
PixelData* pPixel = fastBitmap[(int)linePath.PathPoints[x].X,
(int)linePath.PathPoints[x].Y];

Argb = (pPixel->red<<16)+(pPixel->green<<8)+(pPixel->blue);
pPixel++;

if(Argb == RGBLightBlue)
continue;
else if(Argb == RGBRed)
{
score+=2;
}
else if(Argb == RGBWhite)
{
score+=2;
}
else if(Argb == RGBDarkGreen)
{
score+=4;
}
else
score++;

}
fastBitmap.UnlockBitmap();
}


Regards
Sam



[quoted text, click to view]
codymanix
3/25/2004 12:38:23 PM
What exactly does your method do?

You could use Graphics.DrawPath to draw the path on an empty Bitmap and then
analyze all Pixels on that Bitmap. Or you could set the Clipping Region of
the Graphics objekt to that Path and then use Drawimage and this way filter
out everything that doesn't belong to the path.

--
cody

Freeware Tools, Games and Humour
http://www.deutronium.de.vu || http://www.deutronium.tk

AddThis Social Bookmark Button