简体   繁体   English

.NET 4.5文件读取性能同步与异步

[英].NET 4.5 file read performance sync vs async

We're trying to measure the performance between reading a series of files using sync methods vs async. 我们试图测量使用同步方法和异步读取一系列文件之间的性能。 Was expecting to have about the same time between the two but turns out using async is about 5.5x slower. 期望在两者之间大约有相同的时间但是使用异步的速度大约慢5.5倍。

This might be due to the overhead of managing the threads but just wanted to know your opinion. 这可能是由于管理线程的开销,但只是想了解您的意见。 Maybe we're just measuring the timings wrong. 也许我们只是在测量时间错误。

These are the methods being tested: 这些是正在测试的方法:

    static void ReadAllFile(string filename)
    {
        var content = File.ReadAllBytes(filename);
    }

    static async Task ReadAllFileAsync(string filename)
    {
        using (var file = File.OpenRead(filename))
        {
            using (var ms = new MemoryStream())
            {
                byte[] buff = new byte[file.Length];
                await file.ReadAsync(buff, 0, (int)file.Length);
            }
        }
    }

And this is the method that runs them and starts the stopwatch: 这是运行它们并启动秒表的方法:

    static void Test(string name, Func<string, Task> gettask, int count)
    {
        Stopwatch sw = new Stopwatch();

        Task[] tasks = new Task[count];
        sw.Start();
        for (int i = 0; i < count; i++)
        {
            string filename = "file" + i + ".bin";
            tasks[i] = gettask(filename);
        }
        Task.WaitAll(tasks);
        sw.Stop();
        Console.WriteLine(name + " {0} ms", sw.ElapsedMilliseconds);

    }

Which is all run from here: 这是从这里运行的:

    static void Main(string[] args)
    {
        int count = 10000;

        for (int i = 0; i < count; i++)
        {
            Write("file" + i + ".bin");
        }

        Console.WriteLine("Testing read...!");            

        Test("Read Contents", (filename) => Task.Run(() => ReadAllFile(filename)), count);
        Test("Read Contents Async", (filename) => ReadAllFileAsync(filename), count);

        Console.ReadKey();
    }

And the helper write method: 和帮助器写方法:

    static void Write(string filename)
    {
        Data obj = new Data()
        {
            Header = "random string size here"
        };
        int size = 1024 * 20; // 1024 * 256;

        obj.Body = new byte[size];

        for (var i = 0; i < size; i++)
        {
            obj.Body[i] = (byte)(i % 256);
        }

        Stopwatch sw = new Stopwatch();
        sw.Start();

        MemoryStream ms = new MemoryStream();
        Serializer.Serialize(ms, obj);
        ms.Position = 0;

        using (var file = File.Create(filename))
        {
            ms.CopyToAsync(file).Wait();
        }

        sw.Stop();
        //Console.WriteLine("Writing file {0}", sw.ElapsedMilliseconds); 
    }

The results: 结果:

-Read Contents 574 ms
-Read Contents Async 3160 ms

Will really appreciate if anyone can shed some light on this as we searched the stack and the web but can't really find a proper explanation. 我们真的很感激,如果有人能够在我们搜索堆栈和网页时对此有所了解,但却找不到合适的解释。

There are lots of things wrong with the testing code. 测试代码有很多问题。 Most notably, your "async" test does not use async I/O; 最值得注意的是,您的“异步”测试不使用异步I / O; with file streams, you have to explicitly open them as asynchronous or else you're just doing synchronous operations on a background thread. 对于文件流,您必须将它们显式地打开为异步,否则您只是在后台线程上执行同步操作。 Also, your file sizes are very small and can be easily cached. 此外,您的文件大小非常小,可以轻松缓存。

I modified the test code to write out much larger files, to have comparable sync vs async code, and to make the async code asynchronous: 我修改了测试代码以写出更大的文件,具有可比较的同步和异步代码,并使异步代码异步:

static void Main(string[] args)
{
    Write("0.bin");
    Write("1.bin");
    Write("2.bin");

    ReadAllFile("2.bin"); // warmup

    var sw = new Stopwatch();
    sw.Start();
    ReadAllFile("0.bin");
    ReadAllFile("1.bin");
    ReadAllFile("2.bin");
    sw.Stop();

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

    ReadAllFileAsync("2.bin").Wait(); // warmup

    sw.Restart();
    ReadAllFileAsync("0.bin").Wait();
    ReadAllFileAsync("1.bin").Wait();
    ReadAllFileAsync("2.bin").Wait();
    sw.Stop();

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

    Console.ReadKey();
}

static void ReadAllFile(string filename)
{
    using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false))
    {
        byte[] buff = new byte[file.Length];
        file.Read(buff, 0, (int)file.Length);
    }
}

static async Task ReadAllFileAsync(string filename)
{
    using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
    {
        byte[] buff = new byte[file.Length];
        await file.ReadAsync(buff, 0, (int)file.Length);
    }
}

static void Write(string filename)
{
    int size = 1024 * 1024 * 256;
    var data = new byte[size];
    var random = new Random();
    random.NextBytes(data);
    File.WriteAllBytes(filename, data);
}

On my machine, this test (built in Release, run outside the debugger) yields these numbers: 在我的机器上,此测试(内置于Release,在调试器外部运行)产生以下数字:

Sync: 00:00:00.4461936
Async: 00:00:00.4429566

All I/O Operation are async. 所有I / O操作都是异步的。 The thread just waits(it gets suspended) for I/O operation to finish. 线程只是等待(它被挂起)以完成I / O操作。 That's why when read jeffrey richter he always tells to do i/o async, so that your thread is not wasted by waiting around. 这就是为什么当读jeffrey richter时他总是告诉做i / o异步,这样你的线程不会因等待而浪费。 from Jeffery Ricter 来自Jeffery Ricter

Also creating a thread is not cheap. 创建一个线程并不便宜。 Each thread gets 1 mb of address space reserved for user mode and another 12kb for kernel mode. 每个线程为用户模式保留1 MB的地址空间,为内核模式保留另外12kb的地址空间。 After this the OS has to notify all the dll in system that a new thread has been spawned.Same happens when you destroy a thread. 在此之后,操作系统必须通知系统中的所有dll已经生成了一个新线程。当你销毁一个线程时就会发生这种情况。 Also think about the complexities of context switching 还要考虑上下文切换的复杂性

Found a great SO answer here 在这里找到了很好的答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM