Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi循环速度问题_Delphi_Loops_For Loop_Performance - Fatal编程技术网

Delphi循环速度问题

Delphi循环速度问题,delphi,loops,for-loop,performance,Delphi,Loops,For Loop,Performance,有没有更快的办法?我基本上需要一次将AA-ZZ添加到数千条记录中 仅仅列出35个项目就要花相当长的时间才能完成一份一千个项目的清单 程序Tmainform.BTNSEEDRCLICK(发送方:TObject); 变量 ch,ch2:char; i:整数; 滑道1、滑道2:T管柱; 开始 slist1:=TStringList.Create; slist2:=TStringList.Create; slist1.Text:=queuebox.Items.Text; 对于ch:=“a”到“z”do

有没有更快的办法?我基本上需要一次将AA-ZZ添加到数千条记录中

仅仅列出35个项目就要花相当长的时间才能完成一份一千个项目的清单


程序Tmainform.BTNSEEDRCLICK(发送方:TObject);
变量
ch,ch2:char;
i:整数;
滑道1、滑道2:T管柱;
开始
slist1:=TStringList.Create;
slist2:=TStringList.Create;
slist1.Text:=queuebox.Items.Text;
对于ch:=“a”到“z”do
开始
对于ch2:=“a”到“z”do
开始
//

对于I:=0到slist1。计数-1 do 开始 application.ProcessMessages;//因此它不会在长循环中冻结应用程序。如果有的话,也不是100%确定应该放在哪里。 睡眠(1)//没有这个,它不会处理取消按钮。 如果取消则中断; slist2.Add(slist1.Strings[i]+ch+ch2); 结束; 结束; 结束; insertsingle(slist2,队列框); freeandnil(slist1); freeandnil(slist2);
结束;


感谢您的帮助

我想看看您是否可以根据评论在一个循环中完成。还可以尝试在线程中执行此操作,这样您就可以消除Application.ProcessMessages和Sleep调用,而不会阻塞UI。

您的代码有几个明显的问题

首先,重复计算相同的值会浪费大量CPU周期。AA..ZZ值不会改变,因此无需反复构建它们。试着这样做:创建第三个TStringList。通过你的双循环,用所有可能的AA..ZZ排列填充它。完成后,循环并将此预计算字符串列表与
slist1
中的值合并。你应该会看到一个相当大的推动

(或者,如果时间非常宝贵,可以编写一个小程序,计算排列列表并将其保存到文本文件中,然后将其编译成字符串资源,在运行时加载。)

第二,这可能是你的致命之处,你不应该让ProcessMessages和Sleep调用在最里面的循环中<编码>睡眠(1)听起来像是“睡眠1毫秒”,但Windows不提供这种精度。你最终得到的是“至少睡眠1毫秒”。它会释放CPU,直到Windows重新使用它,这通常在16毫秒左右。因此,您正在将16毫秒的延迟(加上ProcessMessages所需的时间)添加到一个非常紧密的循环中,该循环可能只需要几微秒就可以执行其其余代码


如果您需要这样的东西来保持UI的响应性,那么它应该位于最外层的循环中,而不是内部的循环中,并且您甚至不需要每次迭代都运行它。如果ch mod 100=0,尝试类似于
的方法,然后//在此处睡眠并处理消息
。Craig建议将此任务转移到工作线程也会有所帮助,但前提是您对线程有足够的了解,能够正确完成任务。它们可能很棘手。

您应该用
slist2.BeginUpdate()
slist2.EndUpdate()
包围代码,以阻止TStringList进行额外处理

根据我的经验,如果使用更少的
ProcessMessages();睡眠(1)语句,如其他答案所示


试着把它移到第一个for循环的正下方,看看有什么改进。

好的。我已尝试优化您的代码。对于最终测试,需要一些测试数据

我所做的(包括梅森的大部分想法):

  • 注释掉关于“取消”和“取消”的代码
  • 给类型和变量一个更有意义的全名
  • 使用Delphi使用的名称(“应用程序”而不是“应用程序”,等等)使其可读
  • 在“KeepUIGoing”中加入了一些逻辑
  • 将后缀的计算从主循环移到初始化循环中
  • 使它可以选择使用TStringBuilder(它可以比TStringList快很多,并且从Delphi 2009开始提供)
下面是修改后的代码,让我知道它是否适合你

procedure TForm2.Button1Click(Sender: TObject);
{$define UseStringBuilder}
  procedure KeepUIGoing(SourceListIndex: Integer);
  begin
    if SourceListIndex mod 100 = 0 then
    begin
      Application.ProcessMessages;
      // so it doesn't freeze the application in long loops.  Not 100% sure where this should be placed, if at all.
      Sleep(1);
    end;
  end;
const
  First = 'a';
  Last = 'z';
type
  TRange = First .. Last;
  TSuffixes = array [TRange, TRange] of string;
var
  OuterIndex, InnerIndex: Char;
  SourceListIndex: Integer;
  SourceList, TargetList: TStrings;
  Suffixes: TSuffixes;
  NewLine: string;
{$ifdef UseStringBuilder}
  TargetStringBuilder: TStringBuilder; // could be way faster than TStringList
{$endif UseStringBuilder}
begin
  for OuterIndex := First to Last do
    for InnerIndex := First to Last do
      Suffixes[OuterIndex, InnerIndex] := OuterIndex + InnerIndex;

  SourceList := TStringList.Create;
  TargetList := TStringList.Create;
{$ifdef UseStringBuilder}
  TargetStringBuilder := TStringBuilder.Create();
{$endif UseStringBuilder}
  try
    SourceList.Text := queuebox.Items.Text;
    for OuterIndex := First to Last do
    begin
      for InnerIndex := First to Last do
      begin
        for SourceListIndex := 0 to SourceList.Count - 1 do
        begin
          KeepUIGoing(SourceListIndex);
          // if cancel then
          // Break;
          NewLine := SourceList.Strings[SourceListIndex] + Suffixes[OuterIndex, InnerIndex];
{$ifdef UseStringBuilder}
          TargetStringBuilder.AppendLine(NewLine);
{$else}
          TargetList.Add(NewLine);
{$endif UseStringBuilder}
        end;
      end;
    end;
{$ifdef UseStringBuilder}
    TargetList.Text := TargetStringBuilder.ToString();
{$endif UseStringBuilder}
    // insertsingle(TargetList, queuebox);
  finally
{$ifdef UseStringBuilder}
    FreeAndNil(TargetStringBuilder);
{$endif UseStringBuilder}
    FreeAndNil(SourceList);
    FreeAndNil(TargetList);
  end;
end;

--jeroen

试试这个示例代码-希望这会有点帮助(Delphi 5 Ent./WinXP)

procedure TForm1.按钮1点击(发送方:TObject);
变量
i、 k:整数;
sourceList,destList:TStringList;
ch1,ch2:char;
开始
destList:=TStringList.Create;
sourceList:=TStringList.Create;
//一些示例数据,但我猜您的列表将有1000个+
//参赛作品?
添加('Element1');
添加('Element2');
添加('Element3');
尝试
i:=0;
而我<(26*26)做
开始
如果(i mod 100)=0,则
Application.ProcessMessages;
ch1:=字符(65+(i div 26));
ch2:=char(65+(i mod 26));
对于k:=0到sourceList.Count-1 do
destList.Add(格式(“%s-%s%s”,[sourceList.Strings[k],ch1,ch2]);
公司(一);
结束;
备注1.行.添加字符串(destList);
最后
FreeAndNil(destList);
FreeAndNil(源列表);
结束;
结束;

--Reinhard

我知道这并没有明确回答你的问题,但如果你对Delphi算法感兴趣,Julian Bucknall(的首席技术官)写了一本权威的Delphi算法书

:

  • 第一章:什么是算法
  • 第2章:数组
  • 第3章:链表、堆栈和队列
  • 第四章:搜索
  • 第5章:分类
  • 第六章:随机化算法
  • 第7章:哈希和哈希表
  • 第八章:二叉树
  • 第9章:优先级队列和堆
  • 第10章:状态机和正则表达式
  • 第11章:数据压缩
  • 第12章:高级主题
您还可以获得他的EZDSL(Easy Data Structures Library),用于和。

这是一个如何使用secundary线程的示例
procedure TForm2.Button1Click(Sender: TObject);
{$define UseStringBuilder}
  procedure KeepUIGoing(SourceListIndex: Integer);
  begin
    if SourceListIndex mod 100 = 0 then
    begin
      Application.ProcessMessages;
      // so it doesn't freeze the application in long loops.  Not 100% sure where this should be placed, if at all.
      Sleep(1);
    end;
  end;
const
  First = 'a';
  Last = 'z';
type
  TRange = First .. Last;
  TSuffixes = array [TRange, TRange] of string;
var
  OuterIndex, InnerIndex: Char;
  SourceListIndex: Integer;
  SourceList, TargetList: TStrings;
  Suffixes: TSuffixes;
  NewLine: string;
{$ifdef UseStringBuilder}
  TargetStringBuilder: TStringBuilder; // could be way faster than TStringList
{$endif UseStringBuilder}
begin
  for OuterIndex := First to Last do
    for InnerIndex := First to Last do
      Suffixes[OuterIndex, InnerIndex] := OuterIndex + InnerIndex;

  SourceList := TStringList.Create;
  TargetList := TStringList.Create;
{$ifdef UseStringBuilder}
  TargetStringBuilder := TStringBuilder.Create();
{$endif UseStringBuilder}
  try
    SourceList.Text := queuebox.Items.Text;
    for OuterIndex := First to Last do
    begin
      for InnerIndex := First to Last do
      begin
        for SourceListIndex := 0 to SourceList.Count - 1 do
        begin
          KeepUIGoing(SourceListIndex);
          // if cancel then
          // Break;
          NewLine := SourceList.Strings[SourceListIndex] + Suffixes[OuterIndex, InnerIndex];
{$ifdef UseStringBuilder}
          TargetStringBuilder.AppendLine(NewLine);
{$else}
          TargetList.Add(NewLine);
{$endif UseStringBuilder}
        end;
      end;
    end;
{$ifdef UseStringBuilder}
    TargetList.Text := TargetStringBuilder.ToString();
{$endif UseStringBuilder}
    // insertsingle(TargetList, queuebox);
  finally
{$ifdef UseStringBuilder}
    FreeAndNil(TargetStringBuilder);
{$endif UseStringBuilder}
    FreeAndNil(SourceList);
    FreeAndNil(TargetList);
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
   i,k: Integer;
   sourceList, destList: TStringList;
   ch1, ch2: char;
begin
   destList := TStringList.Create;
   sourceList := TStringList.Create;

   //some sample data but I guess your list will have 1000+
   //entries?
   sourceList.Add('Element1');
   sourceList.Add('Element2');
   sourceList.Add('Element3');

   try
      i := 0;
      while i < (26*26) do
      begin
         if (i mod 100) = 0 then
            Application.ProcessMessages;

         ch1 := char(65 + (i div 26));
         ch2 := char(65 + (i mod 26));

         for k := 0 to sourceList.Count -1 do
            destList.Add(Format('%s-%s%s', [sourceList.Strings[k], ch1, ch2]));
         Inc(i);
      end;

      Memo1.Lines.AddStrings(destList);
   finally
      FreeAndNil(destList);
      FreeAndNil(sourceList);
   end;
end;    
procedure TForm1.btnStartClick(Sender: TObject);
var
  I: Integer;
begin
  //***** Fill the sourcelist
  FSource := TStringList.Create;
  FDestination := TStringList.Create;
  for I := 0 to 9999 do
    FSource.Add(Format('Test%0:d', [I]));

  //***** Create and fire Thread
  FSeeder := TSeeder.Create(FSource, FDestination);
  FSeeder.OnTerminate := DoSeederDone;
  FSeeder.Resume;
end;

procedure TForm1.btnStopClick(Sender: TObject);
begin
  if Assigned(FSeeder) then
    FSeeder.Terminate;
end;

procedure TForm1.DoSeederDone(Sender: TObject);
var
  I, step: Integer;
begin
  I := 0;
  step := 0;
  while I < FDestination.Count do
  begin
    //***** Don't show every item. OutputDebugString is pretty slow.
    OutputDebugString(PChar(FDestination[I]));
    Inc(step);
    Inc(I, step);
  end;
  FSource.Free;
  FDestination.Free;
end;

{ TSeeder }

constructor TSeeder.Create(const source, destination: TStringList);
begin
  //***** Create a suspended, automatically freed Thread object.
  Assert(Assigned(source));
  Assert(Assigned(destination));
  Assert(destination.Count = 0);
  inherited Create(True);
  FreeOnTerminate := True; //***** Triggers the OnTerminate event
  FSource := source;
  FDestination := destination;
end;

procedure TSeeder.Execute;
var
  I, J: Integer;
  AString: string;
begin
  FDestination.BeginUpdate;
  try
    FDestination.Capacity := FSource.Count * 26 * 26;
    for I := 0 to Pred(FSource.Count) do
    begin
      AString := FSource[I];
      for J := 0 to Pred(26 * 26) do
      begin
        FDestination.Add(AString + Char(J div 26 + $41) + Char(J mod 26 + $41));
        if Terminated then Exit;
      end;
    end;
  finally
    FDestination.EndUpdate;
  end;
end;
procedure Tmainform.btnSeederClick(Sender: TObject);
var
  ch,ch2:char;
  i:integer;
  slist1, slist2:TStrings;
begin
  slist1:= TStringList.Create;
  slist2:= TStringList.Create;

  slist1.Text :=queuebox.Items.Text;

  slist2.BeginUpdate() 
     for I := 0 to slist1.Count - 1 do
        begin
        application.ProcessMessages; // so it doesn't freeze the application in long loops.  Not 100% sure where this should be placed, if at all.
         if cancel then Break; 
         slist2.Add(slist1.Strings[i]+'AA');
         slist2.Add(slist1.Strings[i]+'AB');
         slist2.Add(slist1.Strings[i]+'AC');
         ...
         slist2.Add(slist1.Strings[i]+'ZZ');
        end;
slist2.EndUpdate()
insertsingle(slist2,queuebox);
freeandnil(slist1);
freeandnil(slist2);
end;
procedure TForm2.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  FSource := TStringList.Create;
  FDestination := TStringList.Create;

end;
procedure TForm2.Button1Click(Sender: TObject);
var
  I: Integer;
begin
  try
    FSource.BeginUpdate;
    FSource.Clear;
    for I := 0 to 9999 do
      FSource.Add(Format('Test%0:d', [I]));
    BackgroundWorker1.Execute;
  finally
    FSource.EndUpdate;
  end;

end;



procedure TForm2.StopButtonClick(Sender: TObject);
begin
  BackgroundWorker1.Cancel;
end;



procedure TForm2.FormDestroy(Sender: TObject);
begin
 FreeAndNil(FSource);
 FreeAndNil(FDestination);
end;


procedure TForm2.BackgroundWorker1Work(Worker: TBackgroundWorker);
var
  I, J: Integer;
  AString: string;
begin
  FDestination.BeginUpdate;
  try
    FDestination.Capacity := FSource.Count * 26 * 26;
    for I := 0 to Pred(FSource.Count) do
    begin
      AString := FSource[I];
      for J := 0 to Pred(26 * 26) do
      begin
        FDestination.Add(AString + Char(J div 26 + $41) + Char(J mod 26 + $41));
        if Worker.CancellationPending then
          Exit;
      end;
      if I mod 10 = 0 then
        TThread.Sleep(1);
      Worker.ReportProgress((I * 100) div FSource.Count);
    end;
    Worker.ReportProgress(100); // completed

  finally
    FDestination.EndUpdate;
  end;
end;

procedure TForm2.BackgroundWorker1WorkProgress(Worker: TBackgroundWorker;
  PercentDone: Integer);
begin
  ProgressBar1.Position := PercentDone;
end;