Delphi循环速度问题
有没有更快的办法?我基本上需要一次将AA-ZZ添加到数千条记录中 仅仅列出35个项目就要花相当长的时间才能完成一份一千个项目的清单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
程序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;