user3058096
user3058096

Reputation: 21

Referencing stream.Position dramatically increase execution time

Any idea why referencing the Position property of a stream dratically increases IO time ?

The execution time of:

        sw.Restart();
        fs = new FileStream("tmp", FileMode.Open);
        var br = new BinaryReader(fs);
        for (int i = 0; i < n; i++)
        {
            fs.Position+=0; //Should be a NOOP
            a[i] = br.ReadDouble();
        }
        Debug.Write("\n");
        Debug.Write(sw.ElapsedMilliseconds.ToString());
        Debug.Write("\n");
        fs.Close();
        sw.Stop();
        Debug.Write(a[0].ToString() + "\n");
        Debug.Write(a[n - 1].ToString() + "\n");

is ~100 times slower than the equivalent loop without the "fs.Position+=0;". Normally the intention with using Seek (or manipulating the Position property) is to speed up things when you dont need all data in the file. But if, e.g., you only need every second value in the file, it is aparently much faster to read the entire file and discard the data that you dont need, than to skip every second value in the file by moving Stream.Position

Upvotes: 2

Views: 180

Answers (2)

Ondrej Svejdar
Ondrej Svejdar

Reputation: 22074

From the reflector:

public override long Position {
  [SecuritySafeCritical]
  get {
    if (this._handle.IsClosed) {
      __Error.FileNotOpen();
    }
    if (!this.CanSeek) {
      __Error.SeekNotSupported();
    }
    if (this._exposedHandle) {
      this.VerifyOSHandlePosition();
    }
    return this._pos + (long)(this._readPos - this._readLen + this._writePos);
  }
  set {
    if (value < 0L) {
      throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }
    if (this._writePos > 0) {
      this.FlushWrite(false);
    }
    this._readPos = 0;
    this._readLen = 0;
    this.Seek(value, SeekOrigin.Begin);
  }
}

(self explanatory) - essentially every set causes flush and there is no check if you're setting the same value to the position; if you're not happy with the FileStream, make your own stream proxy that would handle position update more gracefully :)

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1502006

You're doing two things:

  • Fetching the position
  • Setting it

It's entirely possible that each of those performs a direct interaction with the underlying Win32 APIs, whereas normally you can read a fair amount of data without having to interop with native code at all, due to buffering.

I'm slightly surprised at the extent to which it's worse, but I'm not surprised that it is worse. I think it would be worth you doing separate tests to find out which has more effect - the read or the write. So write similar code which either just reads or just writes. Note that that should affect the code you write later, but it may satisfy your curiosity a bit further.

You might also try

fs.Seek(0, SeekOrigin.Current);

... which is more likely to be ignored as a genuine no-op. But even so, skipping a single byte using fs.Seek(1, SeekOrigin.Current) may well be expensive again.

Upvotes: 4

Related Questions