ストリーミング ビデオ サーバーからの着信 jpeg フレームを処理する Delphi 6 Pro アプリケーションがあります。コードは機能しますが、最近、時間の経過とともに膨大な数のソフト ページ フォールトが生成されることに気付きました。いくつかの調査を行った後、ページ フォールトは 1 つの特定のグラフィック操作から発生しているように見えます。問題の圧縮されていないビットマップのサイズは 320 x 240 または約 300 KB であるため、大きな画像を処理するためではないことに注意してください。生成されるページ フォールトの数は許容範囲ではありません。1 時間で 1000000 ページ フォールトを簡単に超える可能性があります。
以下に含まれるコードをタイマーで 1 秒間に 10 回実行する、簡素化されたテスト ケースを作成しました。GetBitmap() メソッドで TJpegImage を TBitmap に割り当てようとすると、ページ フォールトが発生するようです。その行をコメントアウトしたため、ページ違反は発生しません。assign() は、圧縮解除されたビットを、GetBitmap() が返す新しく作成されたビットマップにプッシュするときに、TJpegImage の部分で圧縮解除操作をトリガーします。Microsoft の pfmon ユーティリティ (ページ フォールト モニター) を実行すると、RtlFillMemoryUlong に関する膨大な数のソフト ページ フォールト エラー行が表示されるため、メモリ バッファー フィル操作中に発生したように見えます。
1 つの不可解なメモ。どの DLL がどのページ フォールトを引き起こしたかを示す pfmon のレポートの要約部分では、左端の列に DLL 名が表示されません。別のシステムでこれを試しましたが、そこでも発生します。
誰でも修正または回避策を提案できますか? これがコードです。IReceiveBufferForClientSocket は、累積バッファにバイトを保持する単純なクラス オブジェクトであることに注意してください。
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;
// ---------------------------------------------------------------
ありがとう、ロバート