Улучшение производительности BinaryReader - PullRequest
3 голосов
/ 19 октября 2011

В настоящее время я пишу BinaryReader, который кэширует свойства BaseStream.Position и BaseStream.Length. Вот то, что я имею до сих пор:

public class FastBinaryReader
{
    BinaryReader reader;

    public long Length { get; private set; }
    public long Position { get; private set; }

    public FastBinaryReader(Stream stream)
    {
        reader = new BinaryReader(stream);
        Length = stream.Length;
        Position = 0;
    }

    public void Seek(long newPosition)
    {
        reader.BaseStream.Position = newPosition;
        Position = newPosition;
    }

    public byte[] ReadBytes(int count)
    {
        if (Position + count >= Length)
            Position = Length;
        else
            Position += count;

        return reader.ReadBytes(count);
    }

    public void Close()
    {
        reader.Close();
    }
}

Вместо предоставления свойств Length и Position я хотел бы создать свойство BaseStream, которое позволяет мне выставлять свои свойства Position и Length как FastBinaryReader.BaseStream.Position и FastBinaryReader.BaseStream.Length, поэтому что мой существующий код останется совместимым с исходным BinaryReader классом.

Как бы я поступил так?

Ответы [ 2 ]

3 голосов
/ 19 октября 2011

Вот окончательная реализация, если кому-то интересно.Передача этого объекта в виде Stream объекту BinaryReader вместо обычного объекта FileStream дает прирост скорости примерно на 45% на моей машине при чтении 1000-байтовых блоков.Параметр длины является точным только при чтении , поскольку длина считывается в начале и не изменяется.Если вы пишете, он не будет обновляться при изменении длины основного потока.

public class FastFileStream : FileStream
{
    private long _position;
    private long _length;

    public FastFileStream(string path, FileMode fileMode) : base(path, fileMode)
    {
        _position = base.Position;
        _length = base.Length;
    }

    public override long Length
    {
        get { return _length; }
    }

    public override long Position
    {
        get { return _position; }
        set
        {
            base.Position = value;
            _position = value;
        }
    }

    public override long Seek(long offset, SeekOrigin seekOrigin)
    {
        switch (seekOrigin)
        {
            case SeekOrigin.Begin:
                _position = offset;
                break;
            case SeekOrigin.Current:
                _position += offset;
                break;
            case SeekOrigin.End:
                _position = Length + offset;
                break;
        }
        return base.Seek(offset, seekOrigin);
    }

    public override int Read(byte[] array, int offset, int count)
    {
        _position += count;
        return base.Read(array, offset, count);
    }

    public override int ReadByte()
    {
        _position += 1;
        return base.ReadByte();
    }
}
2 голосов
/ 19 октября 2011

Я бы так не поступил, как у вас здесь.

Учтите, что вам необходимо предоставить свойство типа Stream (что такое BinaryReader.BaseStream). Поэтому вам нужно создать свой собственный класс, основанный на Stream. Этот класс должен:

  • взять ссылку на FastBinaryReader, чтобы он мог переопределить Stream.Length и Stream.Offset, делегировав FastBinaryReader элемент
  • взять ссылку на Stream (ту же, что передана в конструкторе FastBinaryReader), чтобы делегировать все другие операции этому потоку (вместо этого вы могли бы иметь эти throw new NotImplementedException(), но вы никогда не знаете, какой метод библиотеки позвонит им!)

Вы можете представить, как это будет выглядеть:

private class StreamWrapper : Stream
{
    private readonly FastBinaryReader reader;

    private readonly Stream baseStream;

    public StreamWrapper(FastBinaryReader reader, Stream baseStream)
    {
        this.reader = reader;
        this.baseStream = baseStream;
    }
    public override long Length
    {
        get { return reader.Length; }
    }

    public override long Position
    {
        get { return reader.Position; }
        set { reader.Position = value; }
    }

    // Override all other Stream virtuals as well
}

Это бы сработало, но мне кажется немного неуклюжим. Логическим продолжением было бы поместить кэширование в StreamWrapper вместо внутри FastBinaryReader:

private class StreamWrapper : Stream
{
    private readonly Stream baseStream;

    public StreamWrapper(Stream baseStream)
    {
        this.baseStream = baseStream;
    }

    public override long Length
    {
        get { /* caching implementation */ }
    }

    public override long Position
    {
        get { /* caching implementation */ }
        set { /* caching implementation */ }
    }

    // Override all other Stream virtuals as well
}

Это позволит вам прозрачно использовать StreamWrapper и сохранить поведение кэширования. Но возникает вопрос: Stream вы работаете с такой тупостью, что он сам не кеширует это?

А если это не так, возможно, увеличение производительности, которое вы видите, является результатом этого оператора if внутри ReadBytes, а не кеширования Length и Position?

...