为什么我的 StringGrid 在输入数万个条目后似乎速度变慢?自由帕斯卡
我使用 Free Pascal 和 Lazarus IDE 编写了一个程序。简而言之,它递归地扫描目录并对每个文件进行“处理”(散列),然后将散列值和文件名输出到 StringGrid 中,该 StringGrid 会随着每个连续的文件而刷新。
它对于最多几千个文件来说效果很好,但是当你达到数万个文件时,它真的会变慢,可能每半秒处理一个文件,即使它只是一个几 Kb 的小文件。
负责代码的主要部分如下。谁能明白为什么当网格中的文件数量超过数万时我的程序会变慢?
procedure TForm1.HashFile(FileIterator: TFileIterator);
var
SizeOfFile : int64;
NameOfFileToHash, fileHashValue, PercentageProgress : string;
FI : TFileIterator; //File Iterator class
SG : TStringGrid;
begin
FI := TFileIterator.Create;
SG := TStringGrid.Create(self);
SizeOfFile := 0;
fileHashValue := '';
if StopScan = FALSE then // If Stop button clicked, cancel scan
begin
NameOfFileToHash := (FileIterator.FileName);
SizeOfFile := FileSize(NameofFileToHash);
StatusBar1.SimpleText := 'Currently Hashing: ' + NameOfFileToHash;
fileHashValue := CalcTheHashFile(NameOfFileToHash); // Custom function, see below
// Now lets update the stringgrid and text fields
// StringGrid Elements:
// Col 0 is FileCounter. Col 1 is File Name. Col 2 is Hash
StringGrid1.rowcount:= FileCounter+1;
StringGrid1.Cells[0,FileCounter] := IntToStr(FileCounter);
Stringgrid1.Cells[1,FileCounter] := NameOfFileToHash;
Stringgrid1.Cells[2,FileCounter] := UpperCase(fileHashValue);
// Dynamically scroll the list so the user always has the most recently hashed
// file insight and expand the columns in lie with their content width
StringGrid1.row := FileCounter;
StringGrid1.col := 1;
StringGrid1.AutoSizeColumns;
// Progress Status Elements: Most of these vars are global vars
NoOfFilesExamined.Caption := IntToStr(FileCounter);
PercentageProgress := IntToStr((FileCounter * 100) DIV NoOfFilesInDir2);
Edit1.Caption := PercentageProgress + '%';
TotalBytesRead := TotalBytesRead + SizeOfFile;
edtTotalBytesExamined.Caption := FormatByteSize(TotalBytesRead);
Application.ProcessMessages;
FileCounter := FileCounter+1;
end;
SG.Free;
FI.Free;
end;
完整的源代码可以从我的 SourceForge 页面获取,https://sourceforge.net/projects/quickhash/ 在“文件”下 -->如果您需要的话,可以使用“源代码”。
任何帮助感谢
泰德
I have written a program using Free Pascal and the Lazarus IDE. In brief, it recursively scans directories and 'does stuff' (hashes) with each file and then it outputs the hash values and the file name into a StringGrid, which is refreshed with each successive file.
It works great for up to a few thousand files, but when you get to the tens of thousands, it really slows down, processing maybe one file every half a second, even if it's only a small file of a few Kb.
The main part of the code responsible is below. Can anyone see why my program slows down when the numbers of files in the grid exceeds tens of thousands?
procedure TForm1.HashFile(FileIterator: TFileIterator);
var
SizeOfFile : int64;
NameOfFileToHash, fileHashValue, PercentageProgress : string;
FI : TFileIterator; //File Iterator class
SG : TStringGrid;
begin
FI := TFileIterator.Create;
SG := TStringGrid.Create(self);
SizeOfFile := 0;
fileHashValue := '';
if StopScan = FALSE then // If Stop button clicked, cancel scan
begin
NameOfFileToHash := (FileIterator.FileName);
SizeOfFile := FileSize(NameofFileToHash);
StatusBar1.SimpleText := 'Currently Hashing: ' + NameOfFileToHash;
fileHashValue := CalcTheHashFile(NameOfFileToHash); // Custom function, see below
// Now lets update the stringgrid and text fields
// StringGrid Elements:
// Col 0 is FileCounter. Col 1 is File Name. Col 2 is Hash
StringGrid1.rowcount:= FileCounter+1;
StringGrid1.Cells[0,FileCounter] := IntToStr(FileCounter);
Stringgrid1.Cells[1,FileCounter] := NameOfFileToHash;
Stringgrid1.Cells[2,FileCounter] := UpperCase(fileHashValue);
// Dynamically scroll the list so the user always has the most recently hashed
// file insight and expand the columns in lie with their content width
StringGrid1.row := FileCounter;
StringGrid1.col := 1;
StringGrid1.AutoSizeColumns;
// Progress Status Elements: Most of these vars are global vars
NoOfFilesExamined.Caption := IntToStr(FileCounter);
PercentageProgress := IntToStr((FileCounter * 100) DIV NoOfFilesInDir2);
Edit1.Caption := PercentageProgress + '%';
TotalBytesRead := TotalBytesRead + SizeOfFile;
edtTotalBytesExamined.Caption := FormatByteSize(TotalBytesRead);
Application.ProcessMessages;
FileCounter := FileCounter+1;
end;
SG.Free;
FI.Free;
end;
The full source code is available from my SourceForge page, https://sourceforge.net/projects/quickhash/ under 'Files' --> 'Source Code' if you need that.
Any help appreciated
Ted
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
作为一名德尔福人,我突然想到了两件事。
自动调整列大小。如果你幸运的话,它什么也不做。如果它沿着 10,000 行中的所有列前进,每次更新并执行 GetTextLength 来查看它是否适合,然后重新绘制网格......
所以工作 1 将是预设一些列大小并将其注释掉。
最多在扫描结束时执行一次。
谁会想要一次查看全部 10,000 多行?
我想我会将它们流式传输到文件中,并显示最后一页完整的内容以指示进度。然后我会使用简单的获取页面完整场景从文件中驱动我的用户界面。
取决于您对数据执行的操作,但您可以重新加载文件以进行进一步分析,对更改进行比较。
即使你坚持在内存中 有一个 TList 吗? THashRecord 的。然后从那里驱动你的显示器,你就有机会了。
Well as a Delphi bloke two things popped out at me.
AutoSizeColumns. If you are lucky it's doing nothing. If it's marching down all columns in 10,000 rows, every update and doing a GetTextLength to see if it fits, then repainting the grid....
So job 1 would be to preset some column sizes and comment that out.
Do it once at the end of the scan at most.
Who is going to want to see all 10,000 + rows at once?
I think I'd stream them out to file, and show the last 1 - page full to indicate progress. Then I'd drive my ui from the file, using a simple get a page full scenario.
Depends on what you are doing with the data, but you could reload file for further analyis, do a compare for changes.
Even if you stick with in memory Have a TList? of THashRecord. Then drive your display from that, you'll have a chance then.
此外,大多数可视组件都有某种方法可以进行批量更新,如下所示:
上面显然是伪代码,但在组件的方法中搜索诸如 BeginUpdate/EndUpdate 之类的内容。使用这些将防止对每一行进行无端处理。即使您希望在填充时更新显示,您也可以每 10 或 100 行更新一次,而不是每行更新一次。
(显然,您可以使用 VirtualListbox 等,而不是像其他人提到的那样让组件管理每一行)。
Also, there is some sort of method for most of the visual components to do batched updates, something like this:
The above is obviously pseudo-code, but search the methods for the component for something like BeginUpdate/EndUpdate. Using these will prevent gratuitous processing for each and every single row. Even if you want the display to update while populating, you could do it every 10 or 100 rows instead of every single row.
(And obviously, you can use a VirtualListbox, etc. instead of giving the component every single row to manage, as others have mentioned).