FileStream.ReadAsync is very slow compared to Read ()

I have the following code for looping through a file and reading 1024 bytes at a time. The first iteration uses FileStream.Read(), and the second iteration uses FileStream.ReadAsync().

private async void Button_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(() => Test()).ConfigureAwait(false);
}

private async Task Test()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();

    int readSize;
    int blockSize = 1024;
    byte[] data = new byte[blockSize];

    string theFile = @"C:\test.mp4";
    long totalRead = 0;

    using (FileStream fs = new FileStream(theFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {

        readSize = fs.Read(data, 0, blockSize);

        while (readSize > 0)
        {
            totalRead += readSize;
            readSize = fs.Read(data, 0, blockSize);
        }
    }

    sw.Stop();
    Console.WriteLine($"Read() Took {sw.ElapsedMilliseconds}ms and totalRead: {totalRead}");

    sw.Reset();
    sw.Start();
    totalRead = 0;
    using (FileStream fs = new FileStream(theFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, (blockSize*2), FileOptions.Asynchronous | FileOptions.SequentialScan))
    {
        readSize = await fs.ReadAsync(data, 0, blockSize).ConfigureAwait(false);

        while (readSize > 0)
        {
            totalRead += readSize;
            readSize = await fs.ReadAsync(data, 0, blockSize).ConfigureAwait(false);
        }
    }

    sw.Stop();
    Console.WriteLine($"ReadAsync() Took {sw.ElapsedMilliseconds}ms and totalRead: {totalRead}");
}

And the result:

Read() Took 162ms and totalRead: 162835040
ReadAsync() Took 15597ms and totalRead: 162835040

ReadAsync () is about 100 times slower. Did I miss something? The only thing I can think of is the overhead for creating and destroying a task using ReadAsync (), but is it overhead?

UPDATE:

I modified the above code to reflect @Cory's suggestion. There is a slight improvement:

Read() Took 142ms and totalRead: 162835040 
ReadAsync() Took 12288ms and totalRead: 162835040

When I increase the reading block size to 1 MB, as suggested by @Alexandru, the results are much more acceptable:

Read() Took 32ms and totalRead: 162835040
ReadAsync() Took 76ms and totalRead: 162835040

, , , . , 100 , .

+4
3

, FileStream, . , , ( , - , async, ), , , (, , 100K , UI-, , , async). , , , . Release, , :

class Program
{
    static void Main(string[] args)
    {
        DoStuff();
        Console.ReadLine();
    }

    public static async void DoStuff()
    {
        var filename = @"C:\Example.txt";

        var sw = new Stopwatch();
        sw.Start();
        ReadAllFile(filename);
        sw.Stop();

        Console.WriteLine("Sync: " + sw.Elapsed);

        sw.Restart();
        await ReadAllFileAsync(filename);
        sw.Stop();

        Console.WriteLine("Async: " + sw.Elapsed);
    }

    static void ReadAllFile(string filename)
    {
        byte[] buffer = new byte[131072];
        using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, buffer.Length, false))
            while (true)
                if (file.Read(buffer, 0, buffer.Length) <= 0)
                    break;
    }

    static async Task ReadAllFileAsync(string filename)
    {
        byte[] buffer = new byte[131072];
        using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, buffer.Length, true))
            while (true)
                if ((await file.ReadAsync(buffer, 0, buffer.Length)) <= 0)
                    break;
    }
}

:

: 00: 00: 00.3092809

Async: 00: 00: 00.5541262

... 1 .

, , 1 , AKA new byte[1048576] ( , 1 ):

: 00: 00: 00.2925763

Async: 00: 00: 00.3402034

. , .

+3

, WPF. , , , . , :

void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() => Button_Click_Impl());
}

async Task Button_Click_Impl()
{
    // put code here.
}

async. , :

new FileStream(theFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096,
               FileOptions.Asynchronous | FileOptions.SequentialScan)

, ConfigureAwait(false), :

readSize = await fs.ReadAsync(data, 0, 1024).ConfigureAwait(false);
+5

ReadAsync , Read ( , . ). , , . , , 32 MiB, , . .

Just start a new task, if for each block there is significant work on binding to the CPU. Otherwise, the user interface must respond to the operation ReadAsync(with a sufficiently large buffer), taking its time (if it completes immediately, you can still block the user interface, see Notes on Task.Yield()).

0
source

Source: https://habr.com/ru/post/1653812/


All Articles