.net中關於非同步效能測試的範例程式碼

來源:互聯網
上載者:User
很久沒有寫部落格了,今年做的產品公司這兩天剛剛開了發布會,稍微清閑下來,想想我們做的產品還有沒有效能最佳化空間,於是想到了.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=10        public 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;        }    }

View Code

其實主要是下面幾行代碼:


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方法的睡眠時間(毫秒),輸入非數字內容退出:10Result:"Hello world,10"1000次 BIO(同步)測試(睡眠10 毫秒):耗時(秒):1.2860545,QPS:    777.571000次 AIO(非同步)測試(睡眠10 毫秒):耗時(秒):0.4895946,QPS:   2042.51請輸入此API方法的睡眠時間(毫秒),輸入非數字內容退出:100Result:"Hello world,100"1000次 BIO(同步)測試(睡眠100 毫秒):耗時(秒):8.2769307,QPS:    120.821000次 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;        }    }

View Code

注意,關鍵代碼只有下面兩個方法:


 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方法的睡眠時間(毫秒),輸入非數字內容退出:10Result:Hello world,101000次 BIO(同步)測試(睡眠10 毫秒):耗時(秒):1.3031966,QPS:    767.341000次 AIO(非同步)測試(睡眠10 毫秒):耗時(秒):0.026441,QPS:  37820.05請輸入此API方法的睡眠時間(毫秒),輸入非數字內容退出:100Result:Hello world,1001000次 BIO(同步)測試(睡眠100 毫秒):耗時(秒):9.8502858,QPS:    101.521000次 AIO(非同步)測試(睡眠100 毫秒):耗時(秒):0.1149469,QPS:   8699.67請輸入線程數:10000請輸入此API方法的睡眠時間(毫秒),輸入非數字內容退出:10Result:Hello world,1010000次 BIO(同步)測試(睡眠10 毫秒):耗時(秒):7.7966125,QPS:   1282.6110000次 AIO(非同步)測試(睡眠10 毫秒):耗時(秒):0.083922,QPS: 119158.27請輸入此API方法的睡眠時間(毫秒),輸入非數字內容退出:100Result:Hello world,10010000次 BIO(同步)測試(睡眠100 毫秒):耗時(秒):34.3646036,QPS:    291.0010000次 AIO(非同步)測試(睡眠100 毫秒):耗時(秒):0.1721833,QPS:  58077.64

結果表示,.NET程式開啟10000個任務(不是10000個原生線程,需要考慮線程池線程),非同步方法呼叫的QPS超過了10萬,而同步方法只有1000多點,效能差距還是很大的。

註:以上測試結果的測試環境是

Intel i7-4790K CPU,4核8線程,記憶體 16GB,Win10 企業版

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.