views:

170

answers:

1

I have a Delphi 6 Pro application that processes incoming jpeg frames from a streaming video server. The code works but I recently noticed that it generates a huge number of soft page faults over time. After doing some investigation, the page faults appear to be coming from one particular graphics operation. Note, the uncompressed bitmaps in question are 320 x 240 or about 300 KB in size so it's not due to the handling of large images. The number of page faults being generated isn't tolerable. Over an hour it can easily top 1000000 page faults.

I created a stripped down test case that executes the code I have included below on a timer, 10 times a second. The page faults appear to happen when I try to assign the TJpegImage to a TBitmap in the GetBitmap() method. I know this because I commented out that line and the page faults do not occur. The assign() triggers a decompression operation on the part of TJpegImage as it pushes the decompressed bits into a newly created bitmap that GetBitmap() returns. When I run Microsoft's pfmon utility (page fault monitor), I get a huge number of soft page fault error lines concerning RtlFillMemoryUlong, so it appears to happen during a memory buffer fill operation.

One puzzling note. The summary part of pfmon's report where it shows which DLL caused what page fault does not show any DLL names in the far left column. I tried this on another system and it happens there too.

Can anyone suggest a fix or a workaround? Here's the code. Note, IReceiveBufferForClientSocket is a simple class object that holds bytes in an accumulating buffer.

function GetBitmap(theJpegImage: TJpegImage): Graphics.TBitmap;
begin
    Result := TBitmap.Create;

    Result.Assign(theJpegImage);
end;

// ---------------------------------------------------------------

procedure processJpegFrame(intfReceiveBuffer: IReceiveBufferForClientSocket);
var
    theBitmap: TBitmap;
    theJpegStream, theBitmapStream: TMemoryStream;
    theJpegImage: TJpegImage;
begin

    theBitmap := nil;
    theJpegImage := TJPEGImage.Create;
    theJpegStream:= TMemoryStream.Create;
    theBitmapStream := TMemoryStream.Create;

    try // 2
        // ************************ BEGIN JPEG FRAME PROCESSING

        // Load the JPEG image from the receive buffer.
        theJpegStream.Size := intfReceiveBuffer.numBytesInBuffer;
        Move(intfReceiveBuffer.bufPtr^, theJpegStream.Memory^, intfReceiveBuffer.numBytesInBuffer);

        theJpegImage.LoadFromStream(theJpegStream);

        // Convert to bitmap.
        theBitmap := GetBitmap(theJpegImage);

    finally
        // Free memory objects.
        if Assigned(theBitmap) then
            theBitmap.Free;
        if Assigned(theJpegImage) then
            theJpegImage.Free;
        if Assigned(theBitmapStream) then
            theBitmapStream.Free;
        if Assigned(theJpegStream) then
            theJpegStream.Free;
    end; // try()
end;

// ---------------------------------------------------------------

procedure TForm1.Timer1Timer(Sender: TObject);
begin
    processJpegFrame(FIntfReceiveBufferForClientSocket);
end;

// ---------------------------------------------------------------

procedure TForm1.FormCreate(Sender: TObject);
var
    S: string;
begin
    FIntfReceiveBufferForClientSocket := TReceiveBufferForClientSocket.Create(1000000);

    S := loadStringFromFile('c:\test.jpg');

    FIntfReceiveBufferForClientSocket.assign(S);
end;

// ---------------------------------------------------------------

Thanks, Robert

A: 

Sounds like the allocations that you allocate and free are not recycled by the memory manager.

Use fastmm, or better, pool them and recycle them yourself.

Marco van de Voort
I am using FastMM with full options on. There's something with the way TJpegImage creates a TBitmap when it decompresses during the Assign() operation that appears to be generating the soft page faults.
Robert Oschler
Still try pooling. If the class goes through some state change, pooling might help, since then the class remains in the same state. Rule of thumb in such cases is always minimize (repeated) dynamic allocations, and then look further.
Marco van de Voort