我正在尝试向我的应用程序界面添加“传输速率:XGb p\min”功能。我正在使用 Lazarus 1.2.2 和 Freepascal 2.6.4 以毫秒级计算它。
我有一个从磁盘读取 64Kb 块的循环,对每个 64Kb 块进行处理,并重复直到读取整个磁盘或直到用户单击中止按钮。我正在尝试计算每次读取 64Kb 需要多长时间,然后计算每分钟读取数据的平均速率,即“每分钟 3Gb”。
我有一个名为“GetTimeInMilliseconds”的自定义函数,它计算标准时间到毫秒。
此外,为了避免界面在部分毫秒内被刷新,我有一个计数器试图确保界面仅在循环中每 50 次刷新一次,即当 64Kb 块被读取 50 次时,计数器重置为 0。
问题是,传输显示要么没有填充,要么填充了不准确的数字,例如 RAID0 设备的“234Mb p\min”!在其他时候,它会得到更真实的东西,比如“3.4Gb p\min”。如果在同一台 PC 和同一磁盘上重复运行,它应该始终准确,而不是不一致不准确。
这是我的 Try...finally 循环,它执行循环。它也称为 FormatByteSize,这是一个自定义函数,可以计算整数大小并将其转换为 XMb、XGb、XTb 等。
var
ProgressCounter, BytesTransferred, BytesPerSecond : integer;
Buffer : array [0..65535] of Byte; // 65536, 64Kb buffer
ctx : TSHA1Context;
Digest : TSHA1Digest;
NewPos, ExactDiskSize, SectorCount, TimeStartRead, TimeEndRead,
MillisecondsElapsed, BytesPerMillisecond, BytesPerMinute : Int64;
StartTime, EndTime, TimeTaken : TDateTime;
...
begin
... // Various stuff related to handles on disks etc
try
SHA1Init(ctx);
FileSeek(hSelectedDisk, 0, 0);
repeat
ProgressCounter := ProgressCounter + 1; // We use this update the progress display occasionally, instead of every buffer read
TimeStartRead := GetTimeInMilliSeconds(Time); // Starting time, in Ms
// The hashing bit...
FileRead(hSelectedDisk, Buffer, 65536); // Read 65536 = 64kb at a time
NewPos := NewPos + SizeOf(Buffer);
SHA1Update(ctx, Buffer, SizeOf(Buffer));
FileSeek(hSelectedDisk, NewPos, 0);
lblBytesLeftToHashB.Caption := IntToStr(ExactDiskSize - NewPos) + ' bytes, ' + FormatByteSize(ExactDiskSize - NewPos);
// End of the hashing bit...
TimeEndRead := GetTimeInMilliSeconds(Time);; // End time in Ms
MillisecondsElapsed := (TimeEndRead - TimeStartRead); // Record how many Ms's have elapsed
// Only if the loop has gone round a while (50 times?), update the progress display
if ProgressCounter = 50 then
begin
if (TimeStartRead > 0) and (TimeEndRead > 0) and (MillisecondsElapsed > 0) then // Only do the divisions and computations if all timings have computed to a positive number
begin
BytesTransferred := SizeOf(Buffer);
BytesPerMillisecond := BytesTransferred DIV MilliSecondsElapsed; // BytesPerMillisecond if often reported as zero, even though BytesTRansferred and MilliSecondsElapsed are valid numbers? Maybe this is the fault?
BytesPerSecond := BytesPerMillisecond * 1000; // Convert the bytes read per millisecond into bytes read per second
BytesPerMinute := BytesPerSecond * 60; // Convert the bytes read per second into bytes read per minute
lblSpeedB.Caption := FormatByteSize(BytesPerMinute) + ' p\min'; // now convert the large "bytes per minute" figure into Mb, Gb or Tb
// Reset the progress counters to zero for another chunk of looping
ProgressCounter := 0;
BytesPerMillisecond := 0;
BytesPerSecond := 0;
BytesPerMinute := 0;
ProgressCounter := 0;
end;
end;
Application.ProcessMessages;
until (NewPos >= ExactDiskSize) or (Stop = true); // Stop looping over the disk
// Compute the final hash value
SHA1Final(ctx, Digest);
lblBytesLeftToHashB.Caption:= '0';
finally
// The handle may have been released by pressing stop. If not, the handle will still be active so lets close it.
if not hSelectedDisk = INVALID_HANDLE_VALUE then CloseHandle(hSelectedDisk);
EndTime := Now;
TimeTaken := EndTime - StartTime;
lblEndTimeB.Caption := FormatDateTime('dd/mm/yy hh:mm:ss', EndTime);
if not stop then edtComputedHash.Text := SHA1Print(Digest);
Application.ProcessMessages;
...
Function GetTimeInMilliSeconds(theTime : TTime): Int64;
// http://www.delphipages.com/forum/archive/index.php/t-135103.html
var
Hour, Min, Sec, MSec: Word;
begin
DecodeTime(theTime,Hour, Min, Sec, MSec);
Result := (Hour * 3600000) + (Min * 60000) + (Sec * 1000) + MSec;
end;
例子:
Form1.Caption := IntToStr(GetTimeInMilliSeconds(Time));