【转】.net异步性能测试(包括ASP.NET MVC WebAPI异步方法)
很久没有写博客了,今年做的产品公司这两天刚刚开了发布会,稍微清闲下来,想想我们做的产品还有没有性能优化空间,于是想到了.Net的异步可以优化性能,但到底能够提升多大的比例呢?恰好有一个朋友正在做各种语言的异步性能测试(有关异步和同步的问题,请参考客《AIO与BIO接口性能对比》),于是我今天写了一个C#的测试程序。
首先,建一个 ASP.NET MVC WebAPI项目,在默认的控制器 values里面,增加两个方法:
// GET api/values?sleepTime=10[HttpGet]public async Task<string> ExecuteAIO(int sleepTime){await Task.Delay(sleepTime);return "Hello world,"+ sleepTime;}[HttpGet]// GET api/values?sleepTime2=10public string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);return "Hello world," + sleepTime2;}
然后,建立一个控制台程序,来测试这个web API:
class Program{static void Main(string[] args){Console.WriteLine("按任意键开始测试 WebAPI:http://localhost:62219/api/values?sleepTime={int}");Console.Write("请输入线程数:");int threadNum = 100;int.TryParse(Console.ReadLine(), out threadNum);while (Test(threadNum)) ;Console.ReadLine();Console.ReadLine();}private static bool Test(int TaskNumber){Console.Write("请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:");string input = Console.ReadLine();int SleepTime = 50;if (!int.TryParse(input, out SleepTime))return false;HttpClient client = new HttpClient();client.BaseAddress = new Uri("http://localhost:62219/");var result = client.GetStringAsync("api/values?sleepTime=" + input).Result;Console.WriteLine("Result:{0}", result);//int TaskNumber = 1000;Console.WriteLine("{0}次 BIO(同步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();sw.Start();Task[] taskArr = new Task[TaskNumber];for (int i = 0; i < TaskNumber; i++){Task task = client.GetStringAsync("api/values?sleepTime2=" + SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime1 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime1, TaskNumber/useTime1);sw.Reset();Console.WriteLine("{0}次 AIO(异步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);sw.Start();for (int i = 0; i < TaskNumber; i++){Task task = client.GetStringAsync("api/values?sleepTime=" + SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime2 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime2, TaskNumber / useTime2);return true;}}
其实主要是下面几行代码:
HttpClient client = new HttpClient(); client.BaseAddress = new Uri("http://localhost:62219/"); var result = client.GetStringAsync("api/values?sleepTime=" + input).Result;
注意,你可能需要使用Nuget添加下面这个包:
Microsoft.AspNet.WebApi.Client
最后,运行这个测试,结果如下:
按任意键开始测试 WebAPI:http://localhost:62219/api/values?sleepTime={int} 请输入线程数:1000 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10 Result:"Hello world,10" 1000次 BIO(同步)测试(睡眠10 毫秒): 耗时(秒):1.2860545,QPS: 777.57 1000次 AIO(异步)测试(睡眠10 毫秒): 耗时(秒):0.4895946,QPS: 2042.51 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100 Result:"Hello world,100" 1000次 BIO(同步)测试(睡眠100 毫秒): 耗时(秒):8.2769307,QPS: 120.82 1000次 AIO(异步)测试(睡眠100 毫秒): 耗时(秒):0.5435111,QPS: 1839.89
本来想尝试测试10000个线程,但报错了。
上面的测试结果,QPS并不高,但由于使用的是IISExpress,不同的Web服务器软件性能不相同,所以还得对比下进程内QPS结果,于是新建一个控制台程序,代码如下:
class Program{static void Main(string[] args){Console.WriteLine("按任意键开始测试 ");Console.Write("请输入线程数:");int threadNum = 100;int.TryParse(Console.ReadLine(), out threadNum);while (Test(threadNum)) ;Console.ReadLine();Console.ReadLine();}private static bool Test(int TaskNumber){Console.Write("请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:");string input = Console.ReadLine();int SleepTime = 50;if (!int.TryParse(input, out SleepTime))return false;var result = ExecuteAIO(SleepTime).Result;Console.WriteLine("Result:{0}", result);//int TaskNumber = 1000;Console.WriteLine("{0}次 BIO(同步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();sw.Start();Task[] taskArr = new Task[TaskNumber];for (int i = 0; i < TaskNumber; i++){Task task = Task.Run<string>(()=> ExecuteBIO(SleepTime));taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime1 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime1, TaskNumber / useTime1);sw.Reset();Console.WriteLine("{0}次 AIO(异步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);sw.Start();for (int i = 0; i < TaskNumber; i++){Task task = ExecuteAIO(SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);sw.Stop();double useTime2 = sw.Elapsed.TotalSeconds;Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime2, TaskNumber / useTime2);return true;}public static async Task<string> ExecuteAIO(int sleepTime){await Task.Delay(sleepTime);return "Hello world," + sleepTime;}public static string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);//不能在非异步方法里面使用 Task.Delay,否则可能死锁//Task.Delay(sleepTime2).Wait();return "Hello world," + sleepTime2;}}
注意,关键代码只有下面两个方法:
public static async Task<string> ExecuteAIO(int sleepTime){await Task.Delay(sleepTime);return "Hello world," + sleepTime;}public static string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);//不能在非异步方法里面使用 Task.Delay,否则可能死锁//Task.Delay(sleepTime2).Wait();return "Hello world," + sleepTime2;}
这两个方法跟WebAPI的测试方法代码是一样的,但是调用代码稍微不同:
同步调用:
Task[] taskArr = new Task[TaskNumber];for (int i = 0; i < TaskNumber; i++){Task task = Task.Run<string>(()=> ExecuteBIO(SleepTime));taskArr[i] = task;}Task.WaitAll(taskArr);
异步调用:
for (int i = 0; i < TaskNumber; i++){Task task = ExecuteAIO(SleepTime);taskArr[i] = task;}Task.WaitAll(taskArr);
可见,这里测试的时候,同步和异步调用,客户端代码都是使用的多线程,主要的区别就是异步方法使用了 async/await 语句。
下面是非Web的进程内异步多线程和同步多线程的结果:
请输入线程数:1000 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10 Result:Hello world,10 1000次 BIO(同步)测试(睡眠10 毫秒): 耗时(秒):1.3031966,QPS: 767.34 1000次 AIO(异步)测试(睡眠10 毫秒): 耗时(秒):0.026441,QPS: 37820.05 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100 Result:Hello world,100 1000次 BIO(同步)测试(睡眠100 毫秒): 耗时(秒):9.8502858,QPS: 101.52 1000次 AIO(异步)测试(睡眠100 毫秒): 耗时(秒):0.1149469,QPS: 8699.67请输入线程数:10000 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10 Result:Hello world,10 10000次 BIO(同步)测试(睡眠10 毫秒): 耗时(秒):7.7966125,QPS: 1282.61 10000次 AIO(异步)测试(睡眠10 毫秒): 耗时(秒):0.083922,QPS: 119158.27 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100 Result:Hello world,100 10000次 BIO(同步)测试(睡眠100 毫秒): 耗时(秒):34.3646036,QPS: 291.00 10000次 AIO(异步)测试(睡眠100 毫秒): 耗时(秒):0.1721833,QPS: 58077.64
结果表示,.NET程序开启10000个任务(不是10000个原生线程,需要考虑线程池线程),异步方法的QPS超过了10万,而同步方法只有1000多点,性能差距还是很大的。
注:以上测试结果的测试环境是
Intel i7-4790K CPU,4核8线程,内存 16GB,Win10 企业版
总结:
不论是普通程序还是Web程序,使用异步多线程,可以极大的提高系统的吞吐量。
后记:
感谢网友“双鱼座” 的提示,我用信号量和都用线程Sleep的方式,对同步和异步方法进行了测试,结果如他所说,TPL异步方式,开销很大,下面是测试数据:
使用 semaphoreSlim 的情况:请输入线程数:1000 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10 Result:Hello world,10 1000次 BIO(同步)测试(睡眠10 毫秒): 耗时(秒):1.2486964,QPS: 800.84 1000次 AIO(异步)测试(睡眠10 毫秒): 耗时(秒):10.5259443,QPS: 95.00 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100 Result:Hello world,100 1000次 BIO(同步)测试(睡眠100 毫秒): 耗时(秒):12.2754003,QPS: 81.46 1000次 AIO(异步)测试(睡眠100 毫秒): 耗时(秒):100.5308431,QPS: 9.95 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:1000 Result:Hello world,1000 1000次 BIO(同步)测试(睡眠1000 毫秒): 耗时(秒):54.0055828,QPS: 18.52 1000次 AIO(异步)测试(睡眠1000 毫秒): 耗时(秒):1000.4749124,QPS: 1.00
使用线程 Sleep的代码改造:
public static async Task<string> ExecuteAIO(int sleepTime){//await Task.Delay(sleepTime);//return "Hello world," + sleepTime;//await Task.Delay(sleepTime);//semaphoreSlim.Wait(sleepTime);System.Threading.Thread.Sleep(sleepTime);return await Task.FromResult("Hello world," + sleepTime);}public static string ExecuteBIO(int sleepTime2){System.Threading.Thread.Sleep(sleepTime2);//semaphoreSlim.Wait(sleepTime2);//不能在非异步方法里面使用 Task.Delay,否则可能死锁//Task.Delay(sleepTime2).Wait();return "Hello world," + sleepTime2;}
运行结果如下:
请输入线程数:1000 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10 Result:Hello world,10 1000次 BIO(同步)测试(睡眠10 毫秒): 耗时(秒):1.3099217,QPS: 763.40 1000次 AIO(异步)测试(睡眠10 毫秒): 耗时(秒):10.9869045,QPS: 91.02 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100 Result:Hello world,100 1000次 BIO(同步)测试(睡眠100 毫秒): 耗时(秒):8.5861461,QPS: 116.47 1000次 AIO(异步)测试(睡眠100 毫秒): 耗时(秒):100.9829406,QPS: 9.90 请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:1000 Result:Hello world,1000 1000次 BIO(同步)测试(睡眠1000 毫秒): 耗时(秒):27.0158904,QPS: 37.02 1000次 AIO(异步)测试(睡眠1000 毫秒):
在每次睡眠1秒的异步方法测试中,很久都没有出来结果,不用考虑,QPS肯定低于一秒了。
经验教训:
在异步方法中,不要使用 Thread.Sleep;在同步方法中,不要使用Task.Delay ,否则可能出现线程死锁,结果难出来。
【转】.net异步性能测试(包括ASP.NET MVC WebAPI异步方法)相关推荐
- 我使用Asp.net MVC WebAPI支持OData协议进行分页操作的笔记(第二篇)
在阅读这篇文章的时候,我想你已经看完第一篇文章啦·也有可能跟我一样,现在正在使用它Asp.net WebAPI为我们干活儿.可能是服务分页查询接口,也可能是其它操作,遇到了一些小问题.有问题,那咱就来 ...
- Asp.net MVC 4 异步方法
前面一篇文章我们介绍了Asp.net MVC 3下异步操作.今天我们来看一下,同样功能在 Asp.net MVC 4 下的实现,基于.net framework 4.5 下的async支持,让我们的代 ...
- ASP.NET MVC WebAPI 资源整理
注:这是收集给公司同事学习的资料,入门级别的. 使用ASP.Net WebAPI构建REST服务(一)--简单的示例 http://blog.csdn.net/mengzhengjie/article ...
- Asp.Net MVC WebAPI的创建与前台Jquery ajax后台HttpClient调用详解
1.什么是WebApi,它有什么用途? Web API是一个比较宽泛的概念.这里我们提到Web API特指ASP.NET MVC Web API.在新出的MVC中,增加了WebAPI,用于提供REST ...
- 我使用Asp.net MVC WebAPI支持OData协议进行分页操作的笔记(第一篇)
OData协议.多么牛B的技术. 传统的分页写习惯了,最近项目中,用到了 Asp.net WebAPI 2.0来做数据交互接口.至于为什么要使用WebAPI,我想只要是对OData协议有了解的朋友.只 ...
- ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml)
我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为json,xml等),但是如果Controller的自动序列化后的结果不是我们想要的该 ...
- Asp.net MVC WebApi Response AOP_se7en3_新浪博客
WebApi项目需要在输出Json时对Json进行编码处理.考虑使用MVC 的AOP 查阅文档需要HttpMessageHandle 新建类CryptDelegatingHandler public ...
- ASP.NET MVC WebAPI实现文件批量上传
工作中学习,学习中记录~~~~~~ 最下面附上demo 任务需求:需要做一个apI接口让C#的程序调用实现批量文件的上传. 难度: 没有做过通过API上传文件的...之前做过网站前后台上传. 首先当然 ...
- C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...
最新文章
- 【rnnoise源码分析】biquad滤波器
- 编译nginx时的两个报错
- 一个小程序:图片代替鼠标移动
- python彩色图像如何进行高斯滤波ValueError: correlate2d inputs must both be 2-D arrays解决方法
- Android处理崩溃的一些实践
- VTK:图片之ImageToPolyDataFilter
- 使用Httpclient来替代客户端的jsonp跨域解决方案
- JavaSE学习之IO流使用技巧
- 一企业彻底实现金融风险数字化,节约人力超4000小时
- 《汇编语言》王爽—实验五详解
- POJ - 3461 (kmp)
- 【数据处理】奇异值分解(SVD) 数据降噪的python实现
- ios首次加载web_iOS预加载Web页面方案
- rabbitmq安装erlang,报错configure: error: /bin/sh ‘/app/otp_src_20.2/erts/configure‘ failed for erts
- 可能最详细的教程,新手如何获取Zcash钱包(ZEC)官方地址的方法
- 创可贴的 ROS PX4 自主飞行无人机 学习笔记(3)
- PHP学习笔记:环境变量
- 【PaddleOCR】一、PaddleOCR安装、测试(Win10)
- 计算机网络技术日趋完善并走向,计算机应用基础 10
- Python中的True和False详解