2 つの異なるアプリケーションで実行される Delphi 2007 コードがいくつかあります。1 つは GUI アプリケーションで、もう 1 つは Windows サービスです。奇妙な点は、GUI アプリケーションには、GUI の描画、統計の計算など、技術的にはより多くの「やるべきこと」があるように見える一方で、Windows サービスは、実行時に一貫してより多くの CPU を使用していることです。GUI アプリケーションが約 3 ~ 4% の CPU 電力を使用する場合、サービスは 6 ~ 8% の範囲で使用されます。
それらを一緒に実行すると、両方のアプリケーションの CPU 負荷は約 2 倍になります。
Windows フォーム アプリケーションに GUI コードが追加されていることを除いて、基本的なコードは両方のアプリケーションで同じです。
この動作には何か理由がありますか? Windows サービス アプリケーションには何らかの固有のオーバーヘッドがありますか、それともコードを調べて、私の本では予期しない動作の原因を見つける必要がありますか?
編集:
コードを詳しく見てみると、GUI アプリケーションが再描画の待機に時間を費やし、CPU 負荷が低下するという以下の提案はおそらく正しくないと思います。アプリケーションは両方ともスレッド化されているため、GUI の再描画が CPU 負荷に影響を与えることはありません。
念のため、最初にアプリケーションからすべての GUI コンポーネントを削除して、空白のフォームだけを残しました。これにより、プログラムの CPU 負荷は増加しませんでした。次に、UI の更新に使用された作業スレッドで Synchronize への呼び出しをすべて削除しました。これは同じ結果でした。CPU 負荷は変化しませんでした。
サービスのコードは次のようになります。
procedure TLsOpcServer.ServiceExecute(Sender: TService);
begin
// Initialize OPC server as NT Service
dmEngine.AddToLog( sevInfo, 'Service', 'Name', Sender.Name );
AddLocalServiceKeysToRegistry( Sender.Name );
dmEngine.AddToLog( sevInfo, 'Service', 'Execute', 'Started' );
dmEngine.Start( True );
//
while not Terminated do
begin
ServiceThread.ProcessRequests( True );
end;
dmEngine.Stop;
dmEngine.AddToLog( sevInfo, 'Service', 'Execute', 'Stopped' );
end;
dmEngine.Start は、OPC サーバーを起動して登録し、ソケットを初期化します。次に、着信 OPC 信号に対して何かを行うスレッドを開始します。GUI アプリケーションのメイン フォームの FormCreate で、まったく同じ呼び出しが行われます。
次に、GUI アプリケーションがどのように起動するかを調べます。このコードは書いていないので、どのように機能するかを理解するのはちょっとした冒険です :)
EDIT2
これは少し興味深いです。両方のアプリケーションをそれぞれ正確に 1 分間実行し、AQTime を実行してそれらのベンチマークを行いました。これは、結果の最も興味深い部分です。
サービス内:
プロシージャ名: TSignalList::HandleChild
実行時間: 20.105963821084
ヒットカウント: 5961231
GUI アプリケーションで:
プロシージャ名: TSignalList::HandleChild
実行時間: 7.62424101324976
ヒット数: 6383010
編集3:
私はようやく、この問題を見続けられる立場に戻ってきました。5 分間の実行でヒット数がほぼ同じである 2 つのプロシージャを見つけましたが、サービスでは実行時間がはるかに長くなります。HandleValue のヒットカウントは 4 300 258 で、サービスの実行時間は 21.77 秒です。GUI アプリケーションのヒットカウントは 4 254 018 で、実行時間は 9.75 秒です。
コードは次のようになります。
function TSignalList.HandleValue(const Signal: string; var Tag: TTag; const CreateIfNotExist: Boolean): HandleStatus;
var
Index: integer;
begin
result := statusNoSignal;
Tag := nil;
if not Assigned( Values ) then
begin
Values := TValueStrings.Create;
Values.CaseSensitive := defDefaultCase;
Values.Sorted := True;
Values.Duplicates := dupIgnore;
Index := -1; // Garantied no items in list
end else
begin
Index := Values.IndexOf( Signal );
end;
if Index = -1 then
begin
if CreateIfNotExist then
begin
// Value signal does not exist create it
Tag := TTag.Create;
if Values.AddObject( Signal, Tag ) > -1 then
begin
result := statusAdded;
end;
end;
end else
begin
Tag := TTag( Values.Objects[ Index ] );
result := statusExist;
end;
end;
どちらのアプリケーションも、まったく同じ回数「CreateIfNotExist」ケースに入ります。TValueStrings は、オーバーロードのない TStringList の直接の子孫です。