65.9K
CodeProject is changing. Read more.
Home

Reducing Page Faults

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.73/5 (3 votes)

Dec 26, 2018

CPOL

1 min read

viewsIcon

5421

downloadIcon

31

Using native GDI will decrease page faults

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