delphi 多线程比单线程慢?

p5cysglq  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(190)

大多数应用程序运行时都在此函数中:

function Panorama(var Solutions: TSolutionDynArray): Integer;
var
  I, J: Integer;
begin
  Result := 0;
  for I := Low(Solutions) to High(Solutions) do
    with Solutions[I] do begin
      Panorama := Level;
      for J := Low(FPertinentPanoramas[I, Level])
          to High(FPertinentPanoramas[I, Level]) do
        if Level > Solutions[FPertinentPanoramas[I, Level, J]].Level then
          Panorama := Panorama + 1
        else
          Panorama := Panorama - 1;
      Result := Result + Panorama;
    end;
end;

其中

TSolution = record
    Level, Panorama: Integer;
  end;
  TSolutionDynArray = array of TSolution;

FPertinentPanoramas是整数的多维数组(初始化一次,然后保持不变)
I, J的计算依赖于任何其他I, J--因此这个问题可以容易地并行化:只需添加参数LowSolutionsHighSolutions并修改循环以

for I := LowSolutions to HighSolutions do

例如:使用多线程1..250、251..500、501..750、751..1000代替1..1000
但是,不管什么原因,单线程总是比多线程快(线程创建一次,然后重用--ExecuteAnonymousThreads; Event.WaitFor;):

type
  TAnonymousThread = class(TThread)
  private
    FEvent: TEvent;
    FProc: TProc;
  protected
    procedure Execute; override;
    procedure SetProc(const Proc: TProc);
  public
    constructor Create(const Proc: TProc = nil);
    property Proc: TProc write SetProc;
  end;

var
  AnonymousThreads: array of TAnonymousThread;
  Event: TEvent;

procedure InitializeThreads(const N: Integer);
procedure ExecuteAnonymousThreads;

implementation

var
  FCriticalSection: TCriticalSection;
  FCounter: Integer;

procedure Counter;
begin
  with FCriticalSection do begin
    Acquire;
    FCounter := FCounter - 1;
    if FCounter = 0 then
      Event.SetEvent;
    Release;
  end;
end;

constructor TAnonymousThread.Create(const Proc: TProc = nil);
begin
  inherited Create;
  FreeOnTerminate := True;
  FEvent := TEvent.Create;
  SetProc(Proc);
end;

procedure TAnonymousThread.Execute;
begin
  while not Terminated do begin
    FEvent.WaitFor;
    FEvent.ResetEvent;
    if Assigned(FProc) then
      FProc;
    Counter;
  end;
end;

procedure TAnonymousThread.SetProc(const Proc: TProc);
begin
  FProc := Proc;
end;

procedure InitializeThreads(const N: Integer);
var
  I: Integer;
begin
  Event := TEvent.Create;
  FCriticalSection := TCriticalSection.Create;
  SetLength(AnonymousThreads, N);
  for I := Low(AnonymousThreads) to High(AnonymousThreads) do
    AnonymousThreads[I] := TAnonymousThread.Create;
end;

procedure ExecuteAnonymousThreads;
var
  I: Integer;
begin
  FCounter := Length(AnonymousThreads);
  Event.ResetEvent;
  for I := Low(AnonymousThreads) to High(AnonymousThreads) do
    AnonymousThreads[I].FEvent.SetEvent;
end;

一个显而易见的问题是,为什么多线程性能如此低下,有什么可以改进的吗?
编辑:

  • 我以前尝试过使用非匿名线程,将Execute硬编码到函数中,但是没有明显的改进,所以我发布了更简单的匿名代码
  • 虽然TParallel.For在函数循环中表现得很糟糕,但现在我函数中**使用它已经得到了令人鼓舞的结果:
TParallel.For(Low(A), High(A),
    procedure(I: Integer)
    begin
      Panorama(SolutionsPool[I]);
    end);
xtupzzrd

xtupzzrd1#

总的来说,我会回答这个问题:为什么一个线程比分成几个线程运行得快?处理器一次执行一个任务,不能同时执行两个任务。Windows也是,但它模拟了多线程。在我们看来,程序同时工作,实际上它们依次工作。你的线程不是同时运行,而是依次运行。条件:每个线程依次执行一个命令,您只需将流拆分为几个并在它们之间添加同步(我没有在你的代码中详细说明,这没有意义)无论你多么努力,你的代码至少会以相同的速度执行(非常值得怀疑)。更快-永远不会。如果你能做得更快,那么它可以在一个线程中实现(会更快)。如果我是你,我会尽量少做线程和它们之间的同步。在新书中,我没有看到对计算机操作的描述,在旧书中有描述。
英特尔处理器汇编语言Kip.R.欧文//第71页

相关问题