-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
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
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
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;
}
}
} |
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 theTask
to 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.ThreadPoolValueTaskSource
was created in the mold of the existing Windows-onlyValueTaskSource
(which was renamed toOverlappedValueTaskSource
) that schedulesRandomAccess
file operations on the thread pool. This class implementsIValueTaskSource
ofint
andlong
(to support both regular and vectored I/O) but alsoIThreadPoolWorkItem
to directly queue itself on the thread pool. Combined with a reusing mechanism likeOverlappedValueTaskSource
's, it allows asyncRandomAccess
I/O to be always amortized allocation-free perSafeFileHandle
, when used non-concurrently.