这似乎工作得很好.
然而,我发现,在16核系统上,我看到处理时间大约为90分钟(它有2,000,000个工作负载;每个都有数据库工作).当我添加16到32个核心时,我看到了性能下降!没有数据库争用.从本质上讲,数据库正在等待事情发生.
每个线程都有自己的DB连接.每个线程的查询仅使用该线程连接.
我更新了Delphi MM以使用Scalemm2;这取得了很大的进步;但我仍然不知道为什么增加核心会降低性能.
当应用程序有256个线程时,在32个核心上,cpu总使用率为80%.
当应用程序有256个线程,在16个核心上,cpu总使用率为100%(这就是为什么我想添加核心) – 它变慢了:-(
我已经尽可能多地应用了我能理解的建议到代码库.
ie – 函数不返回字符串,使用Const作为参数,保护具有小关键部分的“共享”数据(实际上使用多读独占写入).我目前没有分配处理器亲和力;我正在阅读有关使用它的相互矛盾的建议..所以我目前不是(将很难添加,只是今天没有).
问题 – 倾向于我“认为”这个问题围绕线程争用…
如何找到确认线程争用的问题?是否有专门针对此类争用识别的工具?
我怎样才能确定使用“堆”和什么不是什么,以进一步减少那里的争用?
理解,指导,指针将不胜感激.
可以提供相关的代码区域…如果我知道什么是相关的.
Procedure TXETaskWorkloadExecuterThread.Enqueue(Const Workload: TXETaskWorkload); Begin // protect your own queue FWorkloadQueue.Enter; FWorkloads.Add(Workload); FWorkloadQueue.Leave; End; Procedure TXETaskManager.Enqueue(Const Workload: TXETaskWorkload); Begin If FWorkloadCount >= FMaxQueueSize Then Begin WaitForEmptyQueue; FWorkloadCount := 0; End; FExecuters[FNextThread].Enqueue(Workload); // round-robin the queue Inc(FNextThread); Inc(FWorkloadCount); If FNextThread >= FWorkerThreads Then Begin FNextThread := 0; End; End; Function TXETaskWorkloadExecuterThread.Dequeue(Var Workload: TXETaskWorkload): Boolean; Begin Workload := Nil; Result := False; FWorkloadQueue.Enter; Try If FNextWorkload < FWorkloads.Count Then Begin Workload := FWorkloads[FNextWorkload]; Inc(FNextWorkload); If Workload Is TXETaskWorkLoadSynchronize Then Begin FreeAndNil(Workload); Exit; End; Result := True; End Else Begin FWorkloads.Clear; FNextWorkload := 0; FHaveWorkloadInQueue.ResetEvent; FEmptyAndFinishedQueue.SetEvent; End; Finally FWorkloadQueue.Leave; End; End;
编辑—
感谢所有的评论.澄清.
这个系统/ VM没有别的东西.有问题的可执行文件是唯一使用cpu的东西.单线程性能意味着线性.我简单地说这是一个分而治之的.如果我有500万辆汽车要停车,我有30个司机有30个不同的停车场.我可以告诉每个司机等待其他车辆完成停车,这比告诉30名司机同时停放车辆要慢.
单线程中的分析显示没有任何事情导致这种情况.我已经看到在这个板上提到Delphi和多核性能“gotcha’s”(主要与字符串处理和LOCK有关).
数据库本质上说它很无聊,等待事情要做.我查了一份Intels vTune.一般来说,它说……锁.但是,我找不到哪里.我所拥有的内容非常简单,锁定的当前区域是必要且小的.我看不到的是由于其他因素而可能发生的锁定…比如创建锁的字符串,或者线程1通过访问该数据导致主进程出现问题(即使通过关键部分保护).
继续研究.再次感谢您的反馈意见.
解决方法
通常,工作项应存储在单个共享队列中,然后由多个线程拉出.当任何给定的线程准备就绪时,它会拉出下一个可用的工作项.例如:
constructor TXETaskManager.Create; var I: Integer; begin FWorkloadQueue := TCriticalSection.Create; FWorkloads := TList<TXETaskWorkload>.Create; FEmptyQueue := TEvent.Create(nil,True,''); FHaveWorkloadInQueue := TEvent.Create(nil,False,''); FNotFullQueue := TEvent.Create(nil,''); FTermEvent := TEvent.Create(nil,''); ... FMaxQueueSize := ...; FWorkerThreads := ...; for I := 0 to FWorkerThreads-1 do FExecuters[I] := TXETaskWorkloadExecuterThread.Create(Self); end; destructor TXETaskManager.Destroy; begin for I := 0 to FWorkerThreads-1 do FExecuters[I].Terminate; FTermEvent.SetEvent; for I := 0 to FWorkerThreads-1 do begin FExecuters[I].WaitFor; FExecuters[I].Free; end; FWorkloadQueue.Free; FWorkloads.Free; FEmptyQueue.Free; FHaveWorkloadInQueue.Free; FNotFullQueue.Free; FTermEvent.Free; ... inherited; end; procedure TXETaskManager.Enqueue(Const Workload: TXETaskWorkload); begin FWorkloadQueue.Enter; try while FWorkloads.Count >= FMaxQueueSize do begin FWorkloadQueue.Leave; FNotFullQueue.WaitFor(INFINITE); FWorkloadQueue.Enter; end; FWorkloads.Add(Workload); if FWorkloads.Count = 1 then begin FEmptyQueue.ResetEvent; FHaveWorkloadInQueue.SetEvent; end; if FWorkloads.Count >= FMaxQueueSize then FNotFullQueue.ResetEvent; finally FWorkloadQueue.Leave; end; end; function TXETaskManager.Dequeue(var Workload: TXETaskWorkload): Boolean; begin Result := False; Workload := nil; FWorkloadQueue.Enter; try if FWorkloads.Count > 0 then begin Workload := FWorkloads[0]; FWorkloads.Delete(0); Result := True; if FWorkloads.Count = (FMaxQueueSize-1) then FNotFullQueue.SetEvent; if FWorkloads.Count = 0 then begin FHaveWorkloadInQueue.ResetEvent; FEmptyQueue.SetEvent; end; end; finally FWorkloadQueue.Leave; end; end; constructor TXETaskWorkloadExecuterThread.Create(ATaskManager: TXETaskManager); begin inherited Create(False); FTaskManager := ATaskManager; end; procedure TXETaskWorkloadExecuterThread.Execute; var Arr: THandleObjectArray; Event: THandleObject; Workload: TXETaskWorkload; begin SetLength(Arr,2); Arr[0] := FTaskManager.FHaveWorkloadInQueue; Arr[1] := FTaskManager.FTermEvent; while not Terminated do begin case TEvent.WaitForMultiple(Arr,INFINITE,Event) of wrSignaled: begin if Event = FTaskManager.FHaveWorkloadInQueue then begin if FTaskManager.Dequeue(Workload) then try // process Workload as needed... finally Workload.Free; end; end; end; wrError: begin RaiseLastOSError; end; end; end; end;
如果您发现线程没有得到足够的工作,您可以根据需要调整线程数.您通常不应该使用比可用cpu核心多得多的线程.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。