【问题标题】:Windows slowing down with mouse pointer on destopWindows 在桌面上使用鼠标指针变慢
【发布时间】:2021-07-26 15:30:32
【问题描述】:

我有一个奇怪的问题。我开始大约。 160 个进程。现在,如果鼠标指针在桌面上,一些过去需要 100 毫秒的动作现在需要 10 秒,尽管系统的总负载是 13-16%。甚至像 processhacker 这样的第三方程序也会减慢速度并且不会刷新他们的 gui。如果我将鼠标指针移到某个窗口上,无论是哪个窗口(可能是记事本),即使任务栏也可以帮助一切恢复正常。 Processhacker 正在刷新他的列表,并且响应速度回到了 100 毫秒。 由于 Microsoft 支持不会帮助使用 - 因为或进程是在 Borland-Delphi 中编程的,我们不知道如何找出这里发生了什么。 一位同事试图用这个小测试程序重现效果:

unit Unit1;

interface

uses
  Forms,
  ExtCtrls,
  Classes,
  Controls,
  StdCtrls;

const
  DEFAULT_INTERVAL = 31;
  MOD_VALUE = 5;
  MOD_INTERVAL = DEFAULT_INTERVAL * MOD_VALUE;
  DEVIATION_BLACK = 2;
  DEVIATION_RED = 10;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    Timer: TTimer;
    lastTime: TDateTime;
    procedure OnTimer(Sender: TObject);
    procedure SetLabel(lbl: TLabel);
  end;

var
  Form1: TForm1;
  GCounterT: Integer;

implementation

uses
  Windows,
  Graphics,
  SysUtils,
  DateUtils;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.DoubleBuffered := True;

  Timer := TTimer.Create(nil);
  Timer.Interval := DEFAULT_INTERVAL;
  Timer.OnTimer := OnTimer;

  GCounterT := 0;
  lastTime := Now();
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Timer.Free;
end;

procedure TForm1.OnTimer(Sender: TObject);
begin
  Inc(GCounterT);
  if (GCounterT mod MOD_VALUE) = 0 then begin
    SetLabel(Label1);
    GCounterT := 0;
  end;
end;

procedure TForm1.SetLabel(lbl: TLabel);
var
  newValue: string;
  nowTime: TDateTime;
  msDiff: Integer;
  newColor: TColor;
begin
  if IsIconic(Application.Handle) then Exit;

  nowTime := Now();
  msDiff := MilliSecondsBetween(nowTime, lastTime);
  lastTime := nowTime;

  newValue := Format('TTimer:      %s  dev: %d',[FormatDateTime('ss.zzz', nowTime), msDiff - MOD_INTERVAL]);
  if   (msDiff <= (MOD_INTERVAL + DEVIATION_BLACK))
   and (msDiff >= (MOD_INTERVAL - DEVIATION_BLACK)) then
    newColor := clGreen
  else if (msDiff <= (MOD_INTERVAL + DEVIATION_RED))
   and    (msDiff >= (MOD_INTERVAL - DEVIATION_RED)) then
    newColor := clBlack
  else
    newColor := clRed;
  try
    lbl.Font.Color := newColor;
    lbl.Caption := newValue;
  except
  end;
end;

end.

效果不如原始工艺强,但可以重现。 如果一个人启动 180 次,您可以看到相同的效果,只是减速没有那么严重。

8 月 4 日更新: 我添加了 WPA-Analyze-Session 的屏幕截图。在这里可以看到顺序。从 Window 上的鼠标开始,然后是 Desktop、Window、Desktop,最后以 Window 上的鼠标结束。 您可以看到,如果鼠标在桌面上,线程:CSwitch 计数将减少近一半。您还可以看到系统负载始终在 10-17% 之间。

【问题讨论】:

  • 你已经把六吨重的东西放在一头驴身上,你想知道它为什么不能走路。 160 个进程的 31 毫秒计时器间隔 = 队列中每个 WM_TIMER 消息的 193 微秒 CPU 时间。如果您在该处理程序中执行的操作超过 193 微秒,那么您现在没有剩余 CPU 时间,并且 WM_TIMER 消息将延迟或完全丢弃。这不是一个可挽救的设计。您必须重新开始并以不同的方式构建程序。
  • 请注意WM_TIMER 的文档:WM_TIMER 消息是低优先级消息。仅当线程的消息队列中没有其他更高优先级的消息时,GetMessage 和 PeekMessage 函数才发布此消息。 CPU 正在努力绘制(WM_PAINT 的优先级高于 WM_TIMER)是一个死的赠品,你没有剩下更多的 CPU 资源。 160 个进程将使任何桌面系统上的所有内核饱和......将死。您需要以一种更有效的方式重写它。
  • 您的系统中有哪种处理器?我猜你有 4 个插槽,配备 4 个Intel Xeon Platinum 8368 Processor(总共 152 个内核或 304 个线程)。如果没有,你最好买这样的系统:-)
  • 这里的问题不仅仅是消息处理和GUI渲染。这里的问题还在于操作系统需要在这 180 个线程之间不断切换以允许所有应用程序完成它们的工作。在活动线程之间切换确实会带来一些额外的开销。这就是为什么当您制作多线程应用程序时,您不仅要创建任意数量的线程,而且要创建与可用内核一样多的线程。
  • @GreenEyedAndy 我敢肯定,一篇关于 Windows 生态系统内脏的启发性论文在应用于这个特定问题时会很有趣,但它不会改变解决方案 - 数百个快速运行的进程计时器不是一个理智的设计策略,因此您需要完全重新架构。首先考虑一下,这数百个进程在多大程度上实际上甚至需要 UI - 肯定没有用户看到这些标签改变颜色,所以将工作塞进一个线程并可选地显示特定实例的 GUI当且仅当用户想要看到它时。

标签: windows performance delphi windows-server-2019 delphi-2006


【解决方案1】:

在我们设法将调试符号添加到我们的某些进程后,我们在 Delphi-VCL/Forms.pas 中发现了问题。 在带有调试符号的新跟踪中,我们看到 Application.DoMouseIdle 方法花费大量时间查找 VCLWindows、获取这些对象的父对象等等。 减速的根源是“FindDragTarget”方法。我们的流程不需要拖放功能,也不需要在某处显示提示。所以我们从代码中删除了这个函数调用,这并不容易。 现在一切都在快速运行,不受鼠标位置的影响。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-03
    • 2018-08-12
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    • 2013-09-24
    • 1970-01-01
    • 2013-03-17
    相关资源
    最近更新 更多