Introduction
My application does a lot of page faults. From what I found on line, it is unlikely that it should cause any issues, but it got me interested to investigate: what is causing the huge number of page faults, and if there is a way to decrease it.
Background
Few years ago, I was trying to get my 12-year-old son interested in programming (sorry, even kept original name MyFirstGame
). As a result, we designed a simple game.
Please note: This article is not about the game - I am just using the game to investigate page faults counts. While you are playing it, using standard .NET graphical objects, you get about 25000-page faults / seconds.
Using the Code
Code has a simple board (5 rows, 4 columns). Squares are randomly placed on each row, and the player should hit the black square to make it go away. If you did not click on it, and it reached the bottom, you lost, if you hit a white square, you lost. Board is drawn inside of the Form1
object, method "Draw
", using standard .NET.
When you click on "Use GDI", gdi32.dll is used - in method Draw2
. You can see drastic decrease in page faults as you do it.
(To make it easier to observe, I display page faults on screen every second, no need to open Task Manager.)
Also, average draw speed is ~10 times faster using GDI.
Draw
method looks like this:
private void Draw(Graphics g)
{
if (checkBoxUseGDI.Checked)
{
Draw2(g);
return;
}
graphics.Clear(Color.White);
int left = 0;
int top = board.Top;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 4; j++)
{
Rectangle rect = new Rectangle(left, top, 100, 150);
if (board.Cells[i, j] > -1)
{
graphics.FillRectangle(Brushes.Black, rect);
}
graphics.DrawRectangle(Pens.Black, rect);
left += 100;
}
top += 150;
left = 0;
}
g.DrawImage(bitmap, 0, 0);
}
Draw2
method looks like this:
private void Draw2(Graphics g)
{
IntPtr hdc2 = g.GetHdc();
IntPtr hdc = GDI.CreateCompatibleDC(hdc2);
IntPtr hBitmap = GDI.CreateCompatibleBitmap(hdc, 401, 601);
GDI.SelectObject(hdc, hBitmap);
IntPtr brushWhite = GDI.CreateSolidBrush(0xFFFFFF);
GDI.FillRgn(hdc, GDI.CreateRectRgn(0, 0, 401, 601), brushWhite);
GDI.DeleteObject(brushWhite);
IntPtr brushBlack = GDI.CreateSolidBrush(0x0);
int left = 0;
int top = board.Top;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 4; j++)
{
GDI.Rectangle(hdc, left, top, left + 101, top + 151);
if (board.Cells[i, j] > -1)
{
GDI.FillRgn(hdc, GDI.CreateRectRgn(left, top, left + 101, top + 151), brushBlack);
}
left += 100;
}
top += 150;
left = 0;
}
GDI.DeleteObject(brushBlack);
GDI.BitBlt(hdc2, 0, 0, 401, 601, hdc, 0, 0, GDI.TernaryRasterOperations.SRCCOPY);
g.ReleaseHdc(hdc2);
GDI.DeleteDC(hdc);
GDI.DeleteObject(hBitmap);
}
Points of Interest
I found this website to be very helpful: http://pinvoke.net/default.aspx
History
- 26th December, 2018: Initial version