-
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
Asynchronous random file access with FileStream Read vs ReadAsync in a loop / parallel (ReadAsync is slow) #27047
Comments
cc: @stephentoub |
|
@JeremyKuhne Thanks for response Jeremy, Hopefully not too distant future ( 3.1? :) ). This was by far the biggest issue we faced converting our rather large code base from sync to async. It sucked having to convert back and use synchronized code blocks in async functions. |
We have recently invested a LOT in I've fixed the provided benchmarks as they were:
And extended them with:
The code: public class ReadAsyncImprovements
{
const long FileSize = 8000 * 1000; // "we need to retrieve 8000 samples of 1000 bytes"
private readonly byte[] _1kb = new byte[1_000];
private readonly byte[] _8kb = new byte[8_000];
private readonly byte[] _64kb = new byte[64_000];
string _filePath;
[Params(true, false)]
public bool BufferingEnabled { get; set; }
[Params(1_000, 8_000, 64_000)]
public int UserBufferSize { get; set; }
[GlobalSetup]
public void Setup()
{
_filePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
if (File.Exists(_filePath))
{
File.Delete(_filePath);
}
File.WriteAllBytes(_filePath, new byte[FileSize]);
}
[GlobalCleanup]
public void Cleanup() => File.Delete(_filePath);
[Benchmark]
public void ReadSync()
{
// don't allocate the buffer in the benchmark, otherwise allocation stats include it
byte[] userBuffer = UserBufferSize == 1_000 ? _1kb : (UserBufferSize == 8_000 ? _8kb : _64kb);
using FileStream fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferingEnabled ? 4096 : 1, FileOptions.None);
while (fs.Read(userBuffer, 0, userBuffer.Length) > 0) ;
}
[Benchmark]
public async Task ReadAsync()
{
byte[] userBuffer = UserBufferSize == 1_000 ? _1kb : (UserBufferSize == 8_000 ? _8kb : _64kb);
using FileStream fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferingEnabled ? 4096 : 1, FileOptions.Asynchronous);
#if NETFRAMEWORK
while (await fs.ReadAsync(userBuffer, 0, userBuffer.Length) > 0) ;
#else
// it's recommended to use ValueTask-returning overloads
while (await fs.ReadAsync(new Memory<byte>(userBuffer, 0, userBuffer.Length)) > 0) ;
#endif
}
} dotnet run -c Release -f net6.0 --filter *ReadAsyncImprovements* --runtimes net48 net5.0 net6.0 Using my hardware (SSD drive with Windows BitLocker enabled) and your config (buffering enabled, reading data into 1kb array) we can see that
If we disable the buffering and keep using a small buffer (1kb) it's now 3 times slower (9 times for .NET 4.8 and 7 for .NET 5):
But if we increase the array size to 8kb and reduce the number of syscalls we get much better perf:
And using even a bigger array (64kb) we can see that
We are soon going to write a dedicated blog post about our recent improvements. We are also planning to release a doc that explains how to use Since the async |
There seems to be a performance issue with readasync in a loop. It is 8 times slower than the synchronous read. A sample solution is here: https://github.com/virzak/DotNetPerformance
Detailed description of the issuw in SO: https://stackoverflow.com/questions/51560443/asynchronous-random-file-access-with-filestream-read-vs-readasync-in-a-loop-pa
The text was updated successfully, but these errors were encountered: