Delphi中的文件流 - 最佳缓冲区大小

5
我正在使用Delphi RAD Studio XE2。最近我在研究文件流,发现一些有趣的结果,从而引出了这个问题。
在Delphi中,TStreamReader的最佳缓冲区大小是多少?例如,我要加载一个1GB的文件,其中包含200万行,每行都是由doubleTABdoubleTABdouble组成的形式。如果我使用以下代码将其加载到TStringList中,则不同缓冲区大小会导致处理速度和RAM使用情况截然不同。
reader := TStreamReader.Create(fileLocation, TEncoding.UTF8, True, NumBytes);
try
  stringList.BeginUpdate;
  try
    stringList.Clear;
    while not reader.EndOfStream do
      stringList.Add(reader.ReadLine);
    finally
      stringList.EndUpdate;
    end;
  finally
    reader.Free;
  end;
end;

最佳缓冲区大小似乎在1024到4096之间。如果设置小于1024,它似乎会线性减慢并使用更多的RAM。如果设置大于4096,它似乎会指数级减慢。
为什么会出现这些行为,如何确定任务的最佳缓冲区大小?此外,最大缓冲区大小是多少?
编辑
我运行了以下代码来提取使用上述文件大小的运行时间:
startTime := Now();
myStreamReader := TStreamReader.Create(fileLocation, TEncoding.UTF8, True, numBytes);
myStringList := TStringList.Create;
try
  myStringList.BeginUpdate;
  try
    myStringList.Clear;
    while not myStreamReader.EndOfStream do
      myStringList.Add(myStreamReader.ReadLine);
    finally
      myStringList.EndUpdate;
    end;
  finally
    myStreamReader.Free;
  end;
processTime := Now() - startTime;
myStringList.Free;

例子的运行时间如下所示:

Buffer Size 32. Done in 69s
Buffer Size 64. Done in 69s
Buffer Size 96. Done in 69s
Buffer Size 128. Done in 70s
Buffer Size 160. Done in 60s
Buffer Size 192. Done in 57s
Buffer Size 224. Done in 52s
Buffer Size 256. Done in 50s
Buffer Size 512. Done in 44s
Buffer Size 768. Done in 40s
Buffer Size 1024. Done in 39s
Buffer Size 1280. Done in 41s
Buffer Size 1536. Done in 44s
Buffer Size 1792. Done in 40s
Buffer Size 2048. Done in 39s
Buffer Size 2304. Done in 41s
Buffer Size 2560. Done in 41s
Buffer Size 2816. Done in 42s
Buffer Size 3072. Done in 43s
Buffer Size 3328. Done in 43s
Buffer Size 3584. Done in 45s
Buffer Size 3840. Done in 44s
Buffer Size 4096. Done in 45s
Buffer Size 4352. Done in 47s
Buffer Size 4608. Done in 46s
Buffer Size 4864. Done in 46s
Buffer Size 5120. Done in 48s
Buffer Size 5376. Done in 49s
Buffer Size 5632. Done in 51s
Buffer Size 5888. Done in 51s
Buffer Size 6144. Done in 52s
Buffer Size 6400. Done in 54s
Buffer Size 6656. Done in 53s
Buffer Size 6912. Done in 55s
Buffer Size 7168. Done in 55s
Buffer Size 7424. Done in 56s
Buffer Size 7680. Done in 57s
Buffer Size 7936. Done in 65s
Buffer Size 8192. Done in 62s
Buffer Size 8448. Done in 63s
Buffer Size 8704. Done in 64s
Buffer Size 8960. Done in 64s
Buffer Size 9216. Done in 66s
Buffer Size 9472. Done in 66s
Buffer Size 9728. Done in 68s
Buffer Size 9984. Done in 68s
Buffer Size 10240. Done in 69s

关于RAM的使用,缓冲区大小在256以下时,总共使用了大约5GB的RAM,而在1024以上时,总共使用了约3.5GB。例如,使用2kb、4kb和8kb缓冲区的RAM使用情况,请参见下图:

这张图片


1
请参阅TStreamReader-性能差 - LU RD
如果更大的缓冲区速度变慢,那么肯定有问题。这很奇怪。你真的需要加载到字符串列表中吗?如果可以避免,你的速度仍然会更快。 - David Heffernan
1
@DavidHeffernan,在XE7上进行了测试,可以确认TStreamReader的性能不佳。仅使用stringList.LoadFromFile()比它快5倍以上。 - LU RD
这里肯定有些奇怪的事情发生了 - 请参见上面的最近编辑。 - Trojanian
@DavidHeffernan 对于这个特定的问题,我可能不需要一个字符串列表 - 最终我需要将数据传递到一个记录数组中,其中每一行对应一个记录。 - Trojanian
显示剩余2条评论
1个回答

1

@Trojanian,你上面提到的代码类似于Remy Lebeau在你之前的帖子中所提供的答案TStringList.LoadFromFile - Exceptions with Large Text Files。我也尝试了一下Remy的示例,它可以加载更大的文件,但对于较小的文件,性能大约只有TStrings.LoadFromFile的一半。我自己尝试调整缓冲区大小并没有提高性能。

然后我找到了以下代码示例Alternative to TStrings.LoadFromFile or TStringList.LoadFromFile,它使用了一个128kb的缓冲区,并将我的大文件的加载时间减半,相比TStrings.LoadFromFile,即在我使用XE3时比你上面的代码快4倍。


谢谢,我会看一下的。 :-) - Trojanian
2
在那段代码中有一些需要注意的问题。首先,在每次循环迭代中,它会对流执行3次查找操作,这对于大文件流来说可能代价高昂。最好在进入循环之前将当前的“Position”和“Size”检索到本地变量中,然后在循环时使用它们。另外,该循环使用了“TStream.Read()”,但没有检查失败,并假定总是读取了“ReadSize”字节数。最好改用“TStream.ReadBuffer()”。 - Remy Lebeau
感谢Remy的反馈! - Lars
1
@Lars,抱歉这么晚才回复你。我试着玩了一下,发现它比问题中的代码快得多。我还添加了功能来更改缓冲区大小,在链接的代码中发现128kb是最佳的。我对此很感兴趣。个人认为内存管理有点神秘和黑暗。我会去研究一下的。 - Trojanian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接