-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use UnixFileStream's ReadAsync implementation on Windows when !IsAsync #56682
Conversation
UnixFileStream's ReadAsync implementation uses a reusable IValueTaskSource implementation to avoid allocating a new work item on every read. We can push that implementation down to OSFileStreamStrategy, and then use it for the Windows implementation of ReadAsync as well when IsAsync==false, rather than delegating to the base Stream implementation. This PR almost entirely just moves code around. The only change to logic is in RandomAccess.Windows.cs, to only set an offset into the NativeOverlapped if the SafeFileHandle is seekable; otherwise, it fails when used with pipes.
Tagging subscribers to this area: @dotnet/area-system-io Issue DetailsUnixFileStream's ReadAsync implementation uses a reusable IValueTaskSource implementation to avoid allocating a new work item on every read. We can push that implementation down to OSFileStreamStrategy, and then use it for the Windows implementation of ReadAsync as well when IsAsync==false, rather than delegating to the base Stream implementation. This PR almost entirely just moves code around. The only change to logic is in RandomAccess.Windows.cs, to only set an offset into the NativeOverlapped if the SafeFileHandle is seekable; otherwise, it fails when used with pipes.
private byte[] _buffer = new byte[4096];
private FileStream _stream;
[GlobalSetup]
public void Setup()
{
byte[] bytes = new byte[40_000_000];
new Random().NextBytes(bytes);
string path = Path.GetTempFileName();
File.WriteAllBytes(path, bytes);
_stream = File.OpenRead(path);
}
[GlobalCleanup]
public void Cleanup()
{
_stream.Close();
File.Delete(_stream.Name);
}
[Benchmark]
public void Read()
{
_stream.Position = 0;
while (_stream.Read(_buffer) != 0) ;
}
[Benchmark]
public async Task ReadAsync()
{
_stream.Position = 0;
while (await _stream.ReadAsync(_buffer) != 0) ;
} cc: @adamsitnik
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you @stephentoub !
src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs
Show resolved
Hide resolved
return MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> segment) ? | ||
new ValueTask(BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : | ||
base.WriteAsync(buffer, cancellationToken); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after this code removal, SyncWindowsFileStreamStrategy
and UnixFileStreamStrategy
become de facto classes that do not change or extend the behaviour of OSFileStreamStrategy
.
Have you considered the removal of SyncWindowsFileStreamStrategy
and UnixFileStreamStrategy
and using OSFileStreamStrategy
directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No strong opinion. If you'd like to consolidate, go for it. If not, I'm ok keeping the derived types.
I am going to merge it now so I can get updated numbers for the blog post ;) |
I am not sure if this was intentional, but after moving all the logic from |
UnixFileStream's ReadAsync implementation uses a reusable IValueTaskSource implementation to avoid allocating a new work item on every read. We can push that implementation down to OSFileStreamStrategy, and then use it for the Windows implementation of ReadAsync as well when IsAsync==false, rather than delegating to the base Stream implementation.
This PR almost entirely just moves code around. The only change to logic is in RandomAccess.Windows.cs, to only set an offset into the NativeOverlapped if the SafeFileHandle is seekable; otherwise, it fails when used with pipes.
cc: @adamsitnik