Должен ли я использовать TMemoryStream в качестве эффективного буфера перед записью в файл? - PullRequest
4 голосов
/ 22 апреля 2009

Я использую D6 Professional и мне нужно создать текстовый файл в определенном формате из множества небольших строк, уже находящихся в памяти. Из соображений производительности я рассматриваю возможность использования TMemoryStream для сопоставления данных файла и последующей записи его на диск за один раз через TFileStream.

Но у меня есть наполовину забытая память (вероятно, из дней до D6) чтения где-то, что TMemoryStream неэффективен, особенно после того, как он достигает своего значения Capacity. Мои навыки Delphi (и Windows API) не достаточно хороши, чтобы проверить код Classes.pas для себя.

(OFFTOPIC), особенно такой код: (строка 5152 из Classes.pas):
NewCapacity: = (NewCapacity + (MemoryDelta - 1)), а не (MemoryDelta - 1);
(/ OFFTOPIC)

Добавление к моему беспокойству заключается в том, что заключение смежного вопроса Использование MemoryStream для записи в XML
было не для использования TMemoryStream, но не сказал почему - будь то из-за самого TMemoryStream или из-за достаточной буферизации в TFileStream или в драйвере устройства ввода-вывода, или только из-за особенностей кода в вопрос.

Спасибо за любой совет
С уважением,
PhilW.

Ответы [ 5 ]

8 голосов
/ 22 апреля 2009

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):

var
  fstm : tFileSTream;
  fBufStm : tJCLBufferedStream;
  iTicks : Cardinal;
  fModes : word; // for SO formatting.
begin
  fModes := fmOpenReadWrite or fmCreate or fmShareExclusive;

  iTicks := GetTickCount;
  fstm := tFilestream.create('test1.txt',fModes);
  StreamMyObjects( fStm );
  fstm.free;
  ShowMessage('TEST1='+IntToSTr(GetTickCount-iTicks));

  iTicks := GetTickCount;
  fstm := tFilestream.create('test2.txt',fModes);
  fBufStm := tJclBufferedStream.create( fStm );
  StreamMyObjects( fBufStm );
  fBufStm.free;
  fstm.free;
  ShowMessage('TEST2='+IntToSTr(GetTickCount-iTicks));
end;

в моем тесте следующая процедура:

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;

вернул 47 для tFilestream и 16 для tJCLBufferedStream. Без цикла время незначительно, поэтому вам нужно проверить свои данные ... и сколько вы пишете.

8 голосов
/ 22 апреля 2009

Обычный TFileStram также выполняет буферизацию, и этого достаточно для оптимизации ввода-вывода. Помещение MemoryStream впереди только увеличивает накладные расходы.

2 голосов
/ 23 апреля 2009

Как уже упоминалось, добавление TMemoryStream перед TFileStream, вероятно, не приведет вас туда, куда вам нужно.

Джулиан Бакнолл (Julian Bucknall) создал в статье «The Delphi Magazine» набор бесплатных модулей и классов для добавления функциональности к объектам TStream. Я считаю их чрезвычайно полезными в большинстве моих операций с объектами TStream (особенно с TFileStream).

Ссылка, приведенная ниже, относится к поиску кода Google и предоставляет доступ непосредственно к файлу aaStrms.pas. Для использования файла aaStrms.pas вам понадобятся другие модули (aaIntDeq.pas, aaIntList.pas, aaRegEx.pas и aaStrBld.pas) Это все невизуальные классы, поэтому все, что вам нужно сделать, это включить модуль и создать экземпляр класса.

http://www.google.com/codesearch/p?hl=en#7-hAM65i1Xc/disks/dmag70.zip|alfresco/AAStrms.pas&q=lang:pascal%20aaStrms

Я бы предположил, что класс TaaWriteBufferFilter - это тот, который вы хотите использовать.

НТН,

Ryan

2 голосов
/ 22 апреля 2009

Вы можете использовать TStringList и вызвать метод SaveToFile. Преимущество TStringList в том, что память не копируется при добавлении в нее строки.

Другим вариантом является JCL Jedi, класс TJclBufferedStream.

0 голосов
/ 23 апреля 2009

Я бы выяснил, стоит ли создавать собственные буферы ограниченного размера. Медленно записывать небольшие биты в TFileStream (особенно, когда отложенная запись была отключена на томе хранилища), потому что драйвер диска должен был бы найти сектор для записи на каждом маленьком бите. Поэтому, если вы используете буфер размером несколько секторов (безопасное предположение - 512 КБ или 1 МБ) и записываете буфер, когда он заполнен, это ускорит вашу работу, не беспокоясь о том, что делать с гигантским вздутием живота. TMemoryStream.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...