-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Efficient RandomAccess async I/O on the thread pool.
#55123
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
Efficient RandomAccess async I/O on the thread pool.
#55123
Conversation
There isn't much actually.
…ss I/O on the thread pool. And use it in the RandomAccess.ScheduleSync methods instead of wrapping Task.Factory.StartNew in a ValueTask.
|
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
371be95 to
58af87c
Compare
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
stephentoub
left a comment
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.
Thanks.
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
|
Done. Queueing the work item to the thread pool was factored in one method that also captures the |
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
|
Thanks. Can you share perf numbers? |
|
Unfortunately not. 😕 |
12cb48d to
d85dc57
Compare
|
Libraries tests pass, failures seem unrelated. 🙏🏻 |
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Perfolizer.Horology;
using System.Globalization;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;
[MemoryDiagnoser]
public class Program
{
public static void Main(string[] args) =>
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args,
DefaultConfig.Instance.WithSummaryStyle(new SummaryStyle(CultureInfo.InvariantCulture,
printUnitsInHeader: false, sizeUnit: SizeUnit.B, timeUnit: TimeUnit.Nanosecond, printZeroValuesInContent: true)));
private FileStream _stream;
private byte[] _buffer = new byte[1024];
[Params(false, true)]
public bool UseAsync { get; set; }
[GlobalSetup]
public void Setup()
{
_stream = new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 1, (UseAsync ? FileOptions.Asynchronous : FileOptions.None) | FileOptions.DeleteOnClose);
_stream.Write(RandomNumberGenerator.GetBytes(10_000_000));
}
[GlobalCleanup]
public void Cleanup()
{
_stream.Dispose();
}
[Benchmark]
public void SyncRead()
{
var sfh = _stream.SafeFileHandle;
_stream.Position = 0;
int totalRead = 0;
while (true)
{
int bytesRead = RandomAccess.Read(sfh, _buffer, totalRead);
if (bytesRead == 0) break;
totalRead += bytesRead;
}
}
[Benchmark]
public async Task AsyncRead()
{
var sfh = _stream.SafeFileHandle;
_stream.Position = 0;
int totalRead = 0;
while (true)
{
int bytesRead = await RandomAccess.ReadAsync(sfh, _buffer, totalRead);
if (bytesRead == 0) break;
totalRead += bytesRead;
}
}
} |
adamsitnik
left a comment
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.
@teo-tsirpanis big thanks for your contribution! the allocations wins are impressive!
Now I need to update our Fille IO improvements blogs post (which has not been published yet) ;)
Until now, every time an async file I/O operation could not complete truly asynchronously (meaning on Windows when the file handle is not opened for async I/O and on non-Windows always), the operation would be scheduled to run on a thread pool thread. This scheduling used to be performed by calling
Task.Factory.StartNew, and wrapping theTaskto aValueTask.The problem with this approach is that contrary to
ValueTask's philosophy and to Windows' behavior when using async file handles, every async I/O operation on the thread pool would allocate. This PR addresses that.A new class named
SafeFileHandle.ThreadPoolValueTaskSourcewas created in the mold of the existing Windows-onlyValueTaskSource(which was renamed toOverlappedValueTaskSource) that schedulesRandomAccessfile operations on the thread pool. This class implementsIValueTaskSourceofintandlong(to support both regular and vectored I/O) but alsoIThreadPoolWorkItemto directly queue itself on the thread pool. Combined with a reusing mechanism likeOverlappedValueTaskSource's, it allows asyncRandomAccessI/O to be always amortized allocation-free perSafeFileHandle, when used non-concurrently.