异步等待性能? [英] async await performance?

查看:22
本文介绍了异步等待性能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(只是一个理论问题 - 对于非 GUI 应用程序)

假设我有很多 await 的代码:

公共异步任务消费异步(){等待 A();等待 b();等待 c();等待 d();//..}

每个任务可能需要很短的时间,

问题(再次,理论)

可能这样的情况,整体时间处理所有那些释放回线程"和获取线程回"(这里是红色和绿色:)

比单个线程花费更多的时间,它可以在少量延迟的情况下完成所有工作,

我的意思是,我想成为最高效的,但相反,由于所有这些来回切换 - 我实际上失去了生产力.

这种情况会发生吗?

解决方案

Task 对象表示挂起操作的延迟结果.如果您没有任何挂起的操作,则不必使用任务和 async/await.否则,我相信 async/await 代码通常比其裸 TPL ContinueWith 类似物更有效.

让我们做一些计时:

使用系统;使用 System.Threading;使用 System.Threading.Tasks;命名空间控制台应用程序{课程计划{//异步/等待版本静态异步任务Test1Async(任务任务){返回等待任务;}//TPL 版本静态任务Test2Async(任务任务){返回 task.ContinueWith(t=>t. 结果,CancellationToken.None,TaskContinuationOptions.ExecuteSynchronously,TaskScheduler.Default);}static void Tester(字符串名称,Func,Task> func){var sw = new System.Diagnostics.Stopwatch();sw.开始();for (int i = 0; i <10000000; i++){func(Task.FromResult(0)).Wait();}sw.停止();Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);}静态无效主(字符串 [] args){测试员(Test1Async",Test1Async);测试员(Test2Async",Test2Async);}}}

输出:

<前>Test1Async:1582msTest2Async:4975ms

因此,默认情况下,await 延续比 ContinueWith 延续的处理效率更高.让我们稍微优化一下这段代码:

//异步/等待版本静态异步任务Test1Async(任务任务){如果 (task.IsCompleted)返回任务.结果;返回等待任务;}//TPL 版本静态任务Test2Async(任务任务){如果 (task.IsCompleted)返回 Task.FromResult(task.Result);返回 task.ContinueWith(t=>t. 结果,CancellationToken.None,TaskContinuationOptions.ExecuteSynchronously,TaskScheduler.Default);}

输出:

<前>Test1Async:1557msTest2Async:429ms

现在非异步版本获胜.对于 async 版本,我相信这种优化已经由 async/await 基础架构在内部完成.

无论如何,到目前为止我们只处理了已完成的任务(Task.FromResult).我们来介绍一下真正的异步(当然,这次我们会做更少的迭代):

static TaskDoAsync(){var tcs = new TaskCompletionSource();ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(0));返回 tcs.Task;}static void Tester(字符串名称,Func,Task> func){ThreadPool.SetMinThreads(200, 200);var sw = new System.Diagnostics.Stopwatch();sw.开始();for (int i = 0; i <1000000; i++){func(DoAsync()).Wait();}sw.停止();Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);}

输出:

<前>Test1Async:4207msTest2Async:4734ms

现在差异非常小,尽管 async 版本的性能仍然稍好一些.然而我认为这样的收益真的可以忽略不计,与异步操作的实际成本或在 SynchronizationContext.Current != null 时恢复捕获的上下文的成本相当.

最重要的是,如果您处理异步任务,请选择 async/await,如果您有选择,不是出于性能原因,而是出于易用性,可读性和可维护性.

(Just a theoretical question - for non-gui apps)

Assuming I have this code with many awaits:

public async Task<T> ConsumeAsync()
    {
          await A();
          await b();
          await c();
          await d();
          //..
    }

Where each task can take a very short period of time ,

Question (again , theoretical)

There could be a situation where the overall time dealing with all those "releasing back threads" and "fetching threads back" ( red & green here :)

Is taking more time than a single thread which could done all the work with a small amount of delay ,

I mean , I wanted to be the most productive , but instead , since all those switches back and forth - I actually lost productivity.

Can such scenario occur ?

解决方案

A Task object represent the deferred result of a pending operation. You don't have to use tasks and async/await if you don't have any pending operations. Otherwise, I believe async/await code is generally more efficient than its bare TPL ContinueWith analogue.

Let's do some timing:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    class Program
    {
        // async/await version
        static async Task<int> Test1Async(Task<int> task)
        {
            return await task;
        }

        // TPL version
        static Task<int> Test2Async(Task<int> task)
        {
            return task.ContinueWith(
                t => t.Result,
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously,
                TaskScheduler.Default);
        }

        static void Tester(string name, Func<Task<int>, Task<int>> func)
        {
            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            for (int i = 0; i < 10000000; i++)
            {
                func(Task.FromResult(0)).Wait();
            }
            sw.Stop();
            Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
        }

        static void Main(string[] args)
        {
            Tester("Test1Async", Test1Async);
            Tester("Test2Async", Test2Async);
        }
    }
}

The output:

Test1Async: 1582ms
Test2Async: 4975ms

So, by default, await continuations are handled more efficiently than ContinueWith continuations. Let's optimize this code slightly:

// async/await version
static async Task<int> Test1Async(Task<int> task)
{
    if (task.IsCompleted)
        return task.Result;
    return await task;
}

// TPL version
static Task<int> Test2Async(Task<int> task)
{
    if (task.IsCompleted)
        return Task.FromResult(task.Result);

    return task.ContinueWith(
        t => t.Result,
        CancellationToken.None,
        TaskContinuationOptions.ExecuteSynchronously,
        TaskScheduler.Default);
}

The output:

Test1Async: 1557ms
Test2Async: 429ms

Now the non-async version wins. In case with the async version, I believe this optimization has already been done internally by the async/await infrastructure.

Anyway, so far we've dealt only with completed tasks (Task.FromResult). Let's introduce the actual asynchrony (naturally, we'll do less iterations this time):

static Task<int> DoAsync()
{
    var tcs = new TaskCompletionSource<int>();
    ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(0));
    return tcs.Task;
}

static void Tester(string name, Func<Task<int>, Task<int>> func)
{
    ThreadPool.SetMinThreads(200, 200);
    var sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    for (int i = 0; i < 1000000; i++)
    {
        func(DoAsync()).Wait();
    }
    sw.Stop();
    Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}

The output:

Test1Async: 4207ms
Test2Async: 4734ms

Now the difference is very marginal, although the async version still performs slightly better. Yet I think such gain is really neglectable, comparable to the actual cost of the asynchronous operation or to the cost of restoring the captured context for when SynchronizationContext.Current != null.

The bottom line is, if you deal with asynchronous tasks, go for async/await if you have a choice, not for performance reason but for ease of use, readability and maintainability.

这篇关于异步等待性能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆