Оптимальный процесс записи в буферный поток - PullRequest
3 голосов
/ 29 марта 2012

У нас есть собственный алгоритм потоковой передачи данных, который включает некоторые метаданные + записи + значения полей.

В настоящее время мы используем TStream и пишем для добавления значений в поток. Теперь я задаюсь вопросом, можно ли было бы быстрее выполнить эту операцию с использованием некоторой техники.

Редактировать: Мы только добавляем данные в конец, не перемещая и не ища.

Некоторые вещи, о которых я думал:

  • Не используйте Streams, если имеется большой выделенный для памяти буфер для копирования данных, проблема в том, что если мы превысим размер буфера, то нам придется переместиться в какое-то новое пространство памяти.
  • Используйте поток, предварительно заполненный # 0, до некоторого размера, а затем начните добавлять значения. Обоснование состоит в том, что Tstream должен выделять свой собственный буфер каждый раз, когда я делаю запись (я не знаю, как это действительно работает, просто интересно ...)

Мы добавляем строки к TStream и двоичные данные, например, в виде # 0 # 0 # 0 # 1.

Затем данные передаются через TCP, поэтому речь не идет о записи в файл.

Так, каков наилучший метод для этого?

Ответы [ 3 ]

5 голосов
/ 29 марта 2012

Во-первых, вы предполагаете, что TStream является узким местом.Вам нужно профилировать свой код, например, с помощью AQTime, чтобы определить, где на самом деле находится узкое место.Не делайте предположений.

Во-вторых, какой тип TStream вы на самом деле используете?TMemoryStream?TFileStream?Что-то другое?Различные типы потоков обрабатывают память по-разному.TMemoryStream выделяет буфер памяти и увеличивает его на заранее установленное количество байтов всякий раз, когда буфер заполняется.TFileStream, с другой стороны, вообще не использует никакой памяти, он просто записывает непосредственно в файл и позволяет ОС обрабатывать любую буферизацию.

Независимо от того, какой тип потока вы используете, одна вещьВы можете попробовать реализовать собственный пользовательский класс TStream, который имеет внутренний буфер фиксированного размера и указатель на объект TStream вашего реального назначения.Затем вы можете передать экземпляр вашего пользовательского класса в свой алгоритм.Попросите ваш класс переопределить метод TStream::Write(), чтобы скопировать входные данные в его буфер, пока он не заполнится, тогда вы можете Write() буфер до места назначения TStream и очистить буфер.Ваш алгоритм никогда не узнает разницу.И TMemoryStream, и TFileStream выиграют от дополнительной буферизации - меньшее количество больших записей означает более эффективное распределение памяти и файловый ввод / вывод.Например:

type
  TMyBufferedStreamWriter = class(TStream)
  private
    fDest: TStream;
    fBuffer: array[0..4095] of Byte;
    fOffset: Cardinal;
  public
    constructor Create(ADest: TStream);
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    procedure FlushBuffer;
  end;

.

uses
  RTLConsts;

constructor TMyBufferedStreamWriter.Create(ADest: TStream);
begin
  fDest := ADest;
end;

function TMyBufferedStreamWriter.Read(var Buffer; Count: Longint): Longint;
begin
  Result := 0;
end;

function TMyBufferedStreamWriter.Write(const Buffer; Count: Longint): Longint;
var
  pBuffer: PByte;
  Num: Cardinal;
begin
  Result := 0;
  pBuffer := PByte(@Buffer);
  while Count > 0 do
  begin
    Num := Min(SizeOf(fBuffer) - fOffset, Cardinal(Count));
    if Num = 0 then FlushBuffer;
    Move(pBuffer^, fBuffer[fOffset], Num);
    Inc(fOffset, Num);
    Inc(pBuffer, Num);
    Dec(Count, Num);
    Inc(Result, Num);
  end;
end;

procedure TMyBufferedStreamWriter.FlushBuffer;
var
  Idx: Cardinal;
  Written: Longint;
begin
  if fOffset = 0 then Exit;
  Idx := 0;
  repeat
    Written := fDest.Write(fBuffer[Idx], fOffset - Idx);
    if Written < 1 then raise EWriteError.CreateRes(@SWriteError);
    Inc(Idx, Written);
  until Idx = fOffset;
  fOffset := 0;
end;

.

Writer := TMyBufferedStreamWriter.Create(RealStreamHere);
try
  ... write data to Writer normally as needed...
  Writer.FlushBuffer;
finally
  Writer.Free;
end;
4 голосов
/ 29 марта 2012
  1. Используйте профилировщик, чтобы увидеть, где он на самом деле медленный.
  2. Если это действительно из-за многократного перераспределения для увеличения размера потока, вы можете избежать этого, установив для свойства Size достаточно большое предварительное значение.
  3. Единственный случай, когда использование буфера памяти может иметь очевидное значение, если вы используете FileStreams, все другие используемые потоки уже делают это для вас.
2 голосов
/ 30 марта 2012

Перезаписать TMemoryStream и убрать ограничение размера и емкости. И не вызывайте TMemoryStream.Clear, а вызывайте TMemoryStream.SetSize (0)

type
  TMemoryStreamEx = class(TMemoryStream)
  public
    procedure SetSize(NewSize: Longint); override;
    property Capacity;
  end;

implementation

{ TMemoryStreamEx }

procedure TMemoryStreamEx.SetSize(NewSize: Integer);
var
  OldPosition: Longint;
begin
  if NewSize > Capacity then
    inherited SetSize(NewSize)
  else
  begin
    OldPosition := Position;
    SetPointer(Memory, NewSize);
    if OldPosition > NewSize then
      Seek(0, soFromEnd);
  end;
end;
...