2025上半年,最新 AI实践都在这!20+ 应用案例,任听一场议题就值回票价 了解详情
写点什么

使用 Async 和 Await 的代价

  • 2011-10-13
  • 本文字数:2007 字

    阅读完需:约 7 分钟

异步技术能使得应用程序的总吞吐量得到显著提升,但这并不是无偿的。异步函数往往比其同步替代方案稍慢一些,而且如果您不介意采用它还会增加相当大的内存压力的话。Stephen Toub 最近在 MSDN 杂志中一篇题为“异步性能:了解 Async 和 Await 的代价”的文章中讨论了该主题。

相对于本机 C++ 代码而言,托管代码最显著的优势之一就是运行时内联函数(inline function)[1] 的能力。CLR 的 JIT 编译器甚至可以跨程序集内联函数,从而大大降低了调用细粒度方法(OOP 程序员偏爱此类方法)的开销。不幸的是,异步调用的本质意味着不能内联委托(delegates cannot be inlined)。此外,在建立异步调用时还包括不少样板代码。因此,这导致了 Stephen 的第一条建议,“考虑粗粒度,而非细粒度(Think Chunky, Not Chatty)”[2]。就像你正在穿越某个 COM 或 p/invoke 边界一样,相对于许多的小型异步调用而言,你应该会更喜爱少数的大型异步调用。

异步模式下无需开发者显式使用 new 运算符,即可通过多种方式分配内存。如果任其发展,这些内存分配法可能导致过大的内存压力,并且由于垃圾回收器尝试跟进还会导致不必要的延迟。考虑来自 Stream 子类的这个签名及其返回语句:

复制代码
public override async Task<int> ReadAsync()<br></br>return this.Read()

此处没有展示隐式创建 Task 对象,该对象用于包装从 Read 方法中返回的整型值。在 Stephen 的文章中,他展示了如何通过缓存最近的 Task对象及重用该对象来降低内存开销。

导致意外对象分配和保留的另一原因是使用闭包(closures)。C#和 VB 中的闭包是通过匿名类来实现的,匿名类包含匿名方法,而且在方法中声明了异步函数。那些匿名函数所需的本地变量据说被“封闭”(closed over)或“提升” (lifted)到该匿名类中。当每次调用匿名类的父方法时都必须创建一个该类实例。

问题并未就此结束,仍有可能使得额外的内存分配进一步恶化。通常情况下,局部变量所引用的对象是被热切请求的,垃圾回收器(GC)一旦明确那些局部变量在当前函数中将不再被使用时就会回收它们。由于在异步函数中所使用的“局部变量”实际上是某个匿名类中的字段,因此在调用期间它们必须被保留。如果此过程耗时数秒,这对于异步调用而言是很常见的,而该匿名类可能在不经意间被晋升为垃圾回收器中更昂贵的 1 代或 2 代对象 [3]。如果这成为问题,Stephen 建议一旦不再需要那些局部变量就应显式地把它们设置为空引用。

Stephen 所讨论的第三个问题是上下文的概念,特别是同步上下文(synchronization context)和执行上下文(execution context)。他在文章中展示了库代码如何通过使用ConfigureAwait 方法故意忽略同步上下文、以及避免某些必须在执行上下文中捕获的事情来获得性能提升的办法。

译注

[1] 内联函数(inline function),在不同的编程语言中,内联函数(inline function)是指已要求编辑器对其执行内联展开( inline expansion )的函数。换言之,程序员已要求编译器将每处调用某函数的地方都插入完整的函数体,而不是生成代码以便从其定义的地方调用该函数。可以使用 C99 或 C++ 编写内联函数,例如:

复制代码
inline int max(int a, int b)
{
return (a > b) ? a : b;
}

然后,调用语句如下:

复制代码
a = max(x, y);

该语句在编译后,可能被转换成为更直接的计算:

复制代码
a = (x > y) ? x : y;

详见 Inline function

[2] 考虑粗粒度,而非细粒度(Think Chunky, Not Chatty),Chunky 与 Chatty 之争此前多见于“服务协定设计”(service contract design)。唠叨的服务(Chatty Service)趋向于返回简化信息,并使用更细粒度的操作。矮胖的服务(Chunky Service)趋向于返回复杂层次信息,并使用粗粒度的操作。换言之,二者不同之处在于,当返回同样的信息时,唠叨的服务与矮胖的服务相比则需要更多的调用,却增加了返回实际需要的适当信息的灵活性。详见 WCF service contract design

[3] 1 代或 2 代对象,“代”是垃圾回收器用到的概念。提到垃圾回收器就不得不说“托管堆的简化模型”,该模型的规则如下:

  • 所有可进行垃圾回收的对象都分配在一个连续的地址空间范围(托管堆)内。
  • 堆被划分为代 (generation),以便只需查找堆的一小部分就能清除大多数垃圾。
  • 代中的对象大体上均为同龄。
  • 代的编号越高,表示堆的这一片区域所包含的对象越老——这些对象就越有可能是稳定的。最老的对象位于最低的地址内,而新的对象则创建在增加的地址内。
  • 新对象的分配指针标记了内存的已使用(已分配)内存区域和未使用(可用)内存区域之间的边界。
  • 通过删除死对象并将活对象转移到堆的低地址末尾,堆周期性地进行压缩。这就扩展了在创建新对象的图表底部的未使用区域。
  • 对象在内存中的顺序仍然是创建它们的顺序,以便于定位。
  • 在堆中,对象之间永远不会有任何空隙。
  • 只有某些可用空间是已提交的。需要时,操作系统会从“保留的”地址范围中分配更多的内存。

详见“垃圾回收器基础与性能提示”

查看英文原文: The Cost of Async and Await

2011-10-13 04:216814
用户头像

发布了 55 篇内容, 共 20.2 次阅读, 收获喜欢 1 次。

关注

评论

发布
暂无评论
发现更多内容

Leetcode 14天算法挑战 D1-1 #704 二分搜索

米菲爸爸

面试 LeetCode

将node项目部署到云服务器详细教程

CRMEB

百万奖金,首届船海数据智能应用创新大赛正式开赛

科技热闻

淘宝小程序体验优化:数据分析和优化实践

阿里巴巴终端技术

小程序 数据分析 体验优化 性能模型

学习管理管理系统解决方案

低代码小观

学习方法 企业管理 企业管理系统 教育管理 CRM系统

应对“反洗钱”,银丰新融反洗钱自主监测系统为机构保驾护航

华为云开发者联盟

数据库 分布式架构 GaussDB 反洗钱 鲲鹏云

无聊科技正经事周刊(第3期):美团的推荐算法,是在玩火吗?

潘大壮

程序员 周刊 行业趋势 科技周刊

6. 堪比JMeter的.Net压测工具 - Crank 实战篇 - 收集诊断跟踪信息与如何分析瓶颈

MASA技术团队

C# .net 测试 压测 测试工具

浅析mysql性能优化

乌龟哥哥

4月月更

自助洗车加盟!自助洗车品牌大全

共享电单车厂家

自助洗车加盟 自助洗车品牌

加盟自助洗车需要符合什么条件

共享电单车厂家

加盟自助洗车 自助洗车加盟条件

2022中国“SaaS”领域十大趋势

小炮

SaaS SaaS应用 SaaS平台

png、jpg这两种图片格式有什么不同?

InfoQ IT百科

重磅!业界首个云原生批量计算项目Volcano正式晋级为CNCF孵化项目

华为云开发者联盟

云原生 Volcano 批量计算 cncf

24小时自助洗车设备厂家哪家好?

共享电单车厂家

自助洗车机厂家 24小时自助洗车 自助洗车机设备

珠宝行业电子秤串口程序开发

108518

珠宝行业erp 珠宝天平 电子秤

无聊科技正经事周刊(第2期):线上马拉松你会参加吗?

潘大壮

程序员 周刊 科技周刊

活动报名 | 如何基于开源项目 Tapdata PDK,快速完成数据源和目标的开发?

tapdata

数据库

限时好礼 | MongoDB又有新书籍出炉啦!

MongoDB中文社区

mongodb

怎么建立无法删除的文件夹?

InfoQ IT百科

深挖房地产行业数智化转型升级价值,推动地产管理革新

数商云

产业互联网 数字化转型 企业数字化

自助洗车机加盟项目要怎么加盟

共享电单车厂家

自助洗车加盟 自助洗车机项目

怎么转换视频的格式?

InfoQ IT百科

24小时自助洗车加盟怎么收费?

共享电单车厂家

自助洗车加盟 24小时自助洗车

常见的杀毒软件有哪些?

InfoQ IT百科

使用APICloud & 科大讯飞SDK快速实现语音识别功能

YonBuilder低代码开发平台

前端开发 语音识别 APP开发 APICloud 科大讯飞

TASKCTL 用户权限操作设置

敏捷调度TASKCTL

DevOps 分布式 敏捷开发 ETL系统 自动化运维

java高级用法之:绑定CPU的线程Thread-Affinity

程序那些事

Java Netty 程序那些事 4月月更

如何发起 MQTT 亿级连接和千万消息吞吐性能测试

阿里巴巴云原生

百万奖金,首届船海数据智能应用创新大赛正式开赛

科技热闻

怎么添加或删除电脑上的应用软件呢?

InfoQ IT百科

使用Async和Await的代价_.NET_Jonathan Allen_InfoQ精选文章