同步代码的异步重写在循环中执行速度慢20倍

时间:2017-05-02 19:12:45

标签: c# performance async-await

我在这里不要惊慌,所以拜托,请耐心等待! :S

我花了相当多的时间将大块代码从同步(即线程阻塞)重写为异步(即C#6+)。有问题的代码在ASP.NET应用程序中运行,涵盖从低级ADO.NET数据库访问到更高级别工作单元模式的所有内容,最后是用于公共API访问的自定义异步HTTP处理程序 - 完整服务器 - 边堆,可以这么说。重写的主要目的不是优化,而是解开,一般清理,并将代码带到类似于现代和深思熟虑的设计的东西。当然,隐含地假设了优化收益。

一般来说一切都很棒,我对新代码的整体质量非常满意,并且在过去几周的实际测试中显示出了改进的可扩展性。服务器上的CPU和内存负载急剧下降!

那么问题是什么,你可能会问?

好吧,我最近的任务是优化一个仍在使用旧同步代码的简单数据导入。当然,在我尝试将其更改为新的异步代码库以查看会发生什么之前,我并没有花很长时间。

鉴于一切,导入代码非常简单。它基本上是一个循环,它从先前已读入内存的列表中读取项目,将每个项目单独添加到一个工作单元,并通过INSERT语句将其保存到SQL数据库。循环完成后,工作单元将被提交,这使得更改成为永久更改(即,也会提交数据库事务)。

问题是新代码需要 20倍,而期望恰好相反!我已经检查过并经过双重检查,新代码中没有明显的开销可以保证这种迟缓。

具体来说:旧代码可以稳定地导入1100项/秒,而新代码可以管理40项/秒AT BEST(平均而言,它甚至更少,因为速率随时间略有下降)!如果我通过VPN运行相同的测试,那么网络成本将超过其他所有内容,吞吐量在同步时为25项/秒,异步为20。

我已经在SO上看到了多个案例,在类似情况下从同步切换到异步时报告了10-20%的减速,我准备处理像我这样的紧密循环。但是在非网络场景中会受到20倍的惩罚?!那是完全不可接受的!

我最好的行动方式是什么?我该如何解决这个意外问题?

更新

我按照建议在分析器下运行导入。

但是,我不确定结果如何。似乎这个过程花了80%以上的时间......等待。亲眼看看:

Profiler report

在自定义HTTP处理程序中花费的14%对应于IDataReader.Read,这是旧同步API的剩余部分的结果。这仍然需要优化,并且可能在不久的将来减少。无论如何,它与WaitAny成本相比相形见绌,在全同步版本中绝对不存在!

奇怪的是,报告没有显示从我的代码到WaitAny的任何直接调用,这让我觉得这可能是异步/等待基础设施的一部分。我在这个结论中错了吗?我有点希望自己!

令我担心的是,我可能会读错了。我知道异步成本比单线程成本更难理解。最后,WaitAny可能只不过是Windows上“Sytem Idle Process”的等价物 - CPU基础设施的人工表示,它反映了CPU资源的一个空闲百分比。

有人可以帮我点清楚吗?

0 个答案:

没有答案