同样的代码在 Windows 服务中运行比在 GUI 应用程序中运行慢

3
我有一些Delphi 2007代码,可以在两个不同的应用程序中运行,一个是GUI应用程序,另一个是Windows服务。奇怪的是,虽然GUI应用程序技术上似乎有更多"要做的事情",比如绘制GUI、计算某些统计数据等等,但当它运行时,Windows服务始终使用更多的CPU。GUI应用程序使用约3-4%的CPU功率,而服务使用约6-8%左右。
同时运行它们时,两个应用程序的CPU负载大致翻倍。
这两个应用程序的基本代码相同,除了在Windows窗体应用程序中添加了GUI代码之外。
这种行为有什么原因吗?Windows服务应用程序有某种固有的开销,还是我需要查找代码中这种出乎意料的行为的根源?
编辑:
经过仔细查看代码后,我认为下面的建议,即GUI应用程序花费一些时间等待重新绘制,导致CPU负载下降,可能是不正确的。这两个应用程序都是线程化的,这意味着GUI重绘不应该影响CPU负载。
为了确保,我先尝试从应用程序中删除所有GUI组件,只留下一个空白表单。那并没有增加程序的CPU负载。然后,我逐步删除了所有工作线程中用于更新UI的同步调用。这也有相同的结果: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应用程序如何启动,因为我没有编写这段代码,所以尝试弄清楚它的工作原理是一次冒险 :)

编辑2

这有点有趣。我分别运行了两个应用程序一分钟,并使用AQTime对它们进行了基准测试。结果中最有趣的部分如下:

在服务中:

过程名称: TSignalList::HandleChild

执行时间: 20.105963821084

命中次数: 5961231

在GUI应用程序中:

过程名称: TSignalList::HandleChild

执行时间: 7.62424101324976

命中次数: 6383010

编辑3:

我终于回到了可以继续研究这个问题的位置。我找到了两个过程,在五分钟的运行中它们的命中次数大致相同,但在服务中执行时间要长得多。对于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的直接后代,没有任何重载。


5
Windows服务本身并不会使任何操作变慢,但这当然取决于你在做什么。展示一些代码或者解释一下算法以便为我们提供背景信息来尝试理解场景,并且也许有人能够给你提供有用的提示。 - jachguate
在您的服务应用程序中,您将程序逻辑放在哪里?在OnServiceStart、OnServiceExecute还是在不同的线程中? - iPath ツ
1
你详细的解释中并没有表明服务应用程序运行更慢。正如GolezTrol所指出的那样,你需要实际计时执行,以确保它运行得更慢。很可能GUI应用程序花费更多时间等待GUI操作,导致CPU利用率降低;这很可能意味着服务应用程序实际上运行得更 - Disillusioned
1个回答

7

您是否计时了核心功能的执行时间?如果是这样,您是否测量到差异?我认为,如果您这样做,除非将更新GUI等其他功能添加到该核心功能的代码中,否则您不会发现它们之间存在太大的差异。

消耗较少的CPU并不意味着运行速度变慢。GUI应用程序可能更经常地等待重绘,这也取决于GPU(以及可能是系统的其他部分)。因此,GUI应用程序可能会消耗较少的CPU功率,因为CPU在继续下一条指令之前需要等待系统的其他部分。


1
很抱歉在这个话题上一直没有回复,我工作很忙,而且我认为我已经设置了电子邮件通知,以便在有人回答这个帖子时收到通知。我使用AQ时间对这两个进程进行了一些分析,你是对的,服务在任何一个任务上都不会花费更长的时间。我喜欢GUI实际上延迟了其他任务的解释,使CPU负载下降。这可能是一个可以接受的答案!一旦我完成其他更紧迫的问题,我会再做更多的挖掘,但这听起来是合理的! - kling

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接