Delphi 在写入文件之前,我应该使用TMemoryStream作为有效的缓冲区吗?

Delphi 在写入文件之前,我应该使用TMemoryStream作为有效的缓冲区吗?,delphi,file-io,Delphi,File Io,我正在使用D6 Professional,需要从内存中已有的大量小字符串创建一个特定格式的文本文件。出于性能原因,我正在考虑使用TMemoryStream来整理文件数据,然后通过TFileStream一次性将其写入磁盘 但我有一个半遗忘的记忆(可能是在D6之前的日子里)读到过某个地方,TMemoryStream效率低下,特别是在它达到容量大小之后。我的Delphi(和Windows API)技能不足以检查Classes.pas代码 (主题外)特别是这样的代码:(Classes.pas的第5152

我正在使用D6 Professional,需要从内存中已有的大量小字符串创建一个特定格式的文本文件。出于性能原因,我正在考虑使用TMemoryStream来整理文件数据,然后通过TFileStream一次性将其写入磁盘

但我有一个半遗忘的记忆(可能是在D6之前的日子里)读到过某个地方,TMemoryStream效率低下,特别是在它达到容量大小之后。我的Delphi(和Windows API)技能不足以检查Classes.pas代码

(主题外)特别是这样的代码:(Classes.pas的第5152行):
新容量:=(新容量+(MemoryDelta-1))和非(MemoryDelta-1)
(/主题外)

让我更担心的是,一个相关问题的结论
没有使用TMemoryStream,但没有说明原因——无论是因为TMemoryStream本身,还是因为TFileStream或I/O设备驱动程序中有足够的缓冲,或者只是问题代码的细节

谢谢你的建议
问候,

菲尔

普通的TFileStram也会进行缓冲,这足以优化I/O。将MemoryStream放在前面只会增加开销。

您可以使用TStringList,并调用SaveToFile方法。TStringList的一个优点是,在向其添加字符串时不会复制内存


另一个选择是绝地JCL类TJCL缓冲流

TFileStream本身不执行缓冲,缓冲由操作系统处理,通常足以满足大多数目的

我的建议是构建一个方法,将数据写入流,然后将TSTream参数传递给该方法。这样,您可以轻松地测试不同的选项,而不会影响您的程序

例如:

Procedure TForm1.StreamMyObjects(aStream : tStream);
begin
  aStream.Write( MyString[1], Length( MyString ) * SizeOf( Char ));
  aStream.Write( CRLF, Length( CRLF ) * SizeOf( Char ));
  aStream.Write( MyOtherString[1], Length( MyOtherString ) * SizeOf( Char ));
  aStream.Write( CRLF, Length( CRLF ) * SizeOf( Char ));
end;
如前所述,在JCL中,有一个TJclBufferedStream,您可以对其进行测试,看看是否有任何性能优势,这将根据您的写作内容和写作量而有所不同。例如,下面将测试一个TFileStream和一个tJCLBufferedStream,以查看它们之间的区别(是的,我知道我错过了TRY/FINALLY):

在我的测试中,执行以下例行程序:

procedure TForm1.StreamMyObjects(aSTream: tStream);
var
  St : string;
  ix : integer;
begin
  for ix := 0 to 10000 do
    begin
      St := 'This is a string which is written to a stream. ' + IntToStr(ix);
      aStream.Write(st[1], Length(st) * SizeOf(Char) );
    end;
end;

对于tFilestream返回47,对于tJCLBufferedStream返回16。没有循环,时间是无关紧要的,这就是为什么您需要对数据进行测试…以及您的写入量。

我会调查是否值得费心构建一个自己的有限大小缓冲区。
向TFileStream写入少量位的速度很慢(特别是在存储卷上禁用了延迟写入时),因为磁盘驱动程序必须在每个少量位上找到要写入的扇区。因此,如果您使用几个扇区大小的缓冲区(安全猜测为512KB或1MB),并在缓冲区满时写入缓冲区,将加快您的工作速度,不必担心如何处理一个巨大膨胀的TMemoryStream。

正如其他人提到的,在TFileStream前面添加一个TMemoryStream可能无法到达您需要到达的地方

Julian Bucknall在“Delphi杂志”的一篇文章中制作了一组免费单元和类,用于向TStream对象添加功能。我发现它们在我处理TStream对象(尤其是TFileStream)的大部分过程中都非常有用

下面的链接来自google代码搜索,可直接访问aaStrms.pas文件。您将需要其他单位(aaIntDeq.pas、aaIntList.pas、aaRegEx.pas和aaStrBld.pas)来使用aaStrms.pas文件。这些都是非可视类,所以您需要做的就是包含单元并实例化类

我建议您使用TaaWriteBufferFilter类

嗯,


Ryan

Henk,请问您是否确定TFileStream已缓冲?随着时间的推移,我在很多地方都读到,TFileStream并不是buferred,而Delphi的textfile实现是buferred。我的实践证明,-readln非常快,而从TFileStream中读取则不是,尤其是在循环中读取许多小缓冲区时。有几种缓冲文件流的Delphi实现(例如Primoz Gabrijelcic的TGpBufferedStream),这也表明本地文件流没有缓冲。不,Delphi包装器TFileStream不做任何缓冲,但Win32流做(尽管有限)缓冲。“让我们保持简单”的方法总是好的。谢谢你的建议。嗨,菲尔,你最后使用缓冲了吗?我可以通过谷歌浏览这个页面,想知道缓冲是否会加快我对文件的写入速度。我正在向文件中写入数百万个大整数,最后尝试了Ryan链接的TaaWriteBufferFilter,将Win XP Pro上30秒的文件保存时间减少到了6秒左右!因此,我想说,如果您正在进行大量的小型写入,我想说,花几分钟的时间来看看用缓冲流包装您的TFileStream是否有帮助,这绝对值得!感谢:D+1帮助我将26MB二进制文件的30秒写入/读取时间减少到5秒左右!干杯D
procedure TForm1.StreamMyObjects(aSTream: tStream);
var
  St : string;
  ix : integer;
begin
  for ix := 0 to 10000 do
    begin
      St := 'This is a string which is written to a stream. ' + IntToStr(ix);
      aStream.Write(st[1], Length(st) * SizeOf(Char) );
    end;
end;