qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          線程上下文切換的性能損耗測試

          線程上下文切換的性能損耗到底有多少,一直沒有直觀的理解,今天寫個程序測試一下。先看看下面的程序(點擊下載):
            
            ThreadTester是所有Tester的基類。所有的Tester都干的是同樣一件事情,把counter增加到100000000,每次只能加1。
          1: public abstract class ThreadTester
          2:     {
          3:         public const long MAX_COUNTER_NUMBER = 100000000;
          4:
          5:         private long _counter = 0;
          6:
          7:         //獲得計數
          8:         public virtual long GetCounter()
          9:         {
          10:             return this._counter;
          11:         }
          12:
          13:         //增加計數器
          14:         protected virtual void IncreaseCounter()
          15:         {
          16:             this._counter += 1;
          17:         }
          18:
          19:         //啟動測試
          20:         public abstract void Start();
          21:
          22:         //獲得Counter從開始增加到現在的數字所耗的時間
          23:         public abstract long GetElapsedMillisecondsOfIncreaseCounter();
          24:
          25:         //測試是否正在運行
          26:         public abstract bool IsTesterRunning();
          27:     }
          SingleThreadTester是單線程計數。
          1: class SingleThreadTester : ThreadTester
          2:     {
          3:         private Stopwatch _aStopWatch = new Stopwatch();
          4:
          5:         public override void Start()
          6:         {
          7:             _aStopWatch.Start();
          8:
          9:             Thread aThread = new Thread(() => WorkInThread());
          10:             aThread.Start();
          11:         }
          12:
          13:         public override long GetElapsedMillisecondsOfIncreaseCounter()
          14:         {
          15:             return this._aStopWatch.ElapsedMilliseconds;
          16:         }
          17:
          18:         public override bool IsTesterRunning()
          19:         {
          20:             return _aStopWatch.IsRunning;
          21:         }
          22:
          23:         private void WorkInThread()
          24:         {
          25:             while (true)
          26:             {
          27:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
          28:                 {
          29:                     _aStopWatch.Stop();
          30:                     break;
          31:                 }
          32:
          33:                 this.IncreaseCounter();
          34:             }
          35:         }
          36:     }
            TwoThreadSwitchTester是兩個線程交替計數。
          1: class TwoThreadSwitchTester : ThreadTester
          2:     {
          3:         private Stopwatch _aStopWatch = new Stopwatch();
          4:         private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
          5:
          6:         public override void Start()
          7:         {
          8:             _aStopWatch.Start();
          9:
          10:             Thread aThread1 = new Thread(() => Work1InThread());
          11:             aThread1.Start();
          12:
          13:             Thread aThread2 = new Thread(() => Work2InThread());
          14:             aThread2.Start();
          15:         }
          16:
          17:         public override long GetElapsedMillisecondsOfIncreaseCounter()
          18:         {
          19:             return this._aStopWatch.ElapsedMilliseconds;
          20:         }
          21:
          22:         public override bool IsTesterRunning()
          23:         {
          24:             return _aStopWatch.IsRunning;
          25:         }
          26:
          27:         private void Work1InThread()
          28:         {
          29:             while (true)
          30:             {
          31:                 _autoResetEvent.WaitOne();
          32:
          33:                 this.IncreaseCounter();
          34:
          35:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
          36:                 {
          37:                     _aStopWatch.Stop();
          38:                     break;
          39:                 }
          40:
          41:                 _autoResetEvent.Set();
          42:             }
          43:         }
          44:
          45:         private void Work2InThread()
          46:         {
          47:             while (true)
          48:             {
          49:                 _autoResetEvent.Set();
          50:                 _autoResetEvent.WaitOne();
          51:                 this.IncreaseCounter();
          52:
          53:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
          54:                 {
          55:                     _aStopWatch.Stop();
          56:                     break;
          57:                 }
          58:             }
          59:         }
          60:     }
           MultiThreadTester可以指定線程數,多個線程爭搶計數。
          1: class MultiThreadTester : ThreadTester
          2:     {
          3:         private Stopwatch _aStopWatch = new Stopwatch();
          4:         private readonly int _threadCount = 0;
          5:         private readonly object _counterLock = new object();
          6:
          7:         public MultiThreadTester(int threadCount)
          8:         {
          9:             this._threadCount = threadCount;
          10:         }
          11:
          12:         public override void Start()
          13:         {
          14:             _aStopWatch.Start();
          15:
          16:             for (int i = 0; i < _threadCount; i++)
          17:             {
          18:                 Thread aThread = new Thread(() => WorkInThread());
          19:                 aThread.Start();
          20:             }
          21:         }
          22:
          23:         public override long GetElapsedMillisecondsOfIncreaseCounter()
          24:         {
          25:             return this._aStopWatch.ElapsedMilliseconds;
          26:         }
          27:
          28:         public override bool IsTesterRunning()
          29:         {
          30:             return _aStopWatch.IsRunning;
          31:         }
          32:
          33:         private void WorkInThread()
          34:         {
          35:             while (true)
          36:             {
          37:                 lock (_counterLock)
          38:                 {
          39:                     if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
          40:                     {
          41:                         _aStopWatch.Stop();
          42:                         break;
          43:                     }
          44:
          45:                     this.IncreaseCounter();
          46:                 }
          47:             }
          48:         }
          49:     }
            Program的Main函數中,根據用戶的選擇來決定執行哪個測試類。
          1: class Program
          2:     {
          3:         static void Main(string[] args)
          4:         {
          5:
          6:             string inputText = GetUserChoice();
          7:
          8:             while (!"4".Equals(inputText))
          9:             {
          10:                 ThreadTester tester = GreateThreadTesterByInputText(inputText);
          11:                 tester.Start();
          12:
          13:                 while (true)
          14:                 {
          15:                     Console.WriteLine(GetStatusOfThreadTester(tester));
          16:                     if (!tester.IsTesterRunning())
          17:                     {
          18:                         break;
          19:                     }
          20:                     Thread.Sleep(100);
          21:                 }
          22:
          23:                 inputText = GetUserChoice();
          24:             }
          25:
          26:             Console.Write("Click enter to exit...");
          27:         }
          28:
          29:         private static string GetStatusOfThreadTester(ThreadTester tester)
          30:         {
          31:             return string.Format("[耗時{0}ms] counter = {1}, {2}",
          32:                     tester.GetElapsedMillisecondsOfIncreaseCounter(), tester.GetCounter(),
          33:                     tester.IsTesterRunning() ? "running" : "stopped");
          34:         }
          35:
          36:         private static ThreadTester GreateThreadTesterByInputText(string inputText)
          37:         {
          38:             switch (inputText)
          39:             {
          40:                 case "1":
          41:                     return new SingleThreadTester();
          42:                 case "2":
          43:                     return new TwoThreadSwitchTester();
          44:                 default:
          45:                     return new MultiThreadTester(100);
          46:             }
          47:         }
          48:
          49:         private static string GetUserChoice()
          50:         {
          51:             Console.WriteLine(@"==Please select the option in the following list:==
          52: 1. SingleThreadTester
          53: 2. TwoThreadSwitchTester
          54: 3. MultiThreadTester
          55: 4. Exit");
          56:
          57:             string inputText = Console.ReadLine();
          58:
          59:             return inputText;
          60:         }
          61:     }
          三個測試類,運行結果如下:
          Single Thread:
          [耗時407ms] counter = 100000001, stopped
          [耗時453ms] counter = 100000001, stopped
          [耗時412ms] counter = 100000001, stopped
          Two Thread Switch:
          [耗時161503ms] counter = 100000001, stopped
          [耗時164508ms] counter = 100000001, stopped
          [耗時164201ms] counter = 100000001, stopped
          Multi Threads - 100 Threads:
          [耗時3659ms] counter = 100000001, stopped
          [耗時3950ms] counter = 100000001, stopped
          [耗時3720ms] counter = 100000001, stopped
          Multi Threads - 2 Threads:
          [耗時3078ms] counter = 100000001, stopped
          [耗時3160ms] counter = 100000001, stopped
          [耗時3106ms] counter = 100000001, stopped
            什么是線程上下文切換
            上下文切換的精確定義可以參考: http://www.linfo.org/context_switch.html。多任務系統往往需要同時執行多道作業。作業數往往大于機器的CPU數,然而一顆CPU同時只能執行一項任務,為了讓用戶感覺這些任務正在同時進行,操作系統的設計者巧妙地利用了時間片輪轉的方式,CPU給每個任務都服務一定的時間,然后把當前任務的狀態保存下來,在加載下一任務的狀態后,繼續服務下一任務。任務的狀態保存及再加載,這段過程就叫做上下文切換。時間片輪轉的方式使多個任務在同一顆CPU上執行變成了可能,但同時也帶來了保存現場和加載現場的直接消耗。(Note. 更精確地說, 上下文切換會帶來直接和間接兩種因素影響程序性能的消耗. 直接消耗包括: CPU寄存器需要保存和加載, 系統調度器的代碼需要執行, TLB實例需要重新加載, CPU 的pipeline需要刷掉; 間接消耗指的是多核的cache之間得共享數據, 間接消耗對于程序的影響要看線程工作區操作數據的大小).
            
            根據上面上下文切換的定義,我們做出下面的假設:
            之所以TwoThreadSwitchTester執行速度最慢,因為線程上下文切換的次數最多,時間主要消耗在上下文切換了,兩個線程交替計數,每計數一次就要做一次線程切換。
            “Multi Threads - 100 Threads”比“Multi Threads - 2 Threads”開的線程數量要多,導致線程切換次數也比后者多,執行時間也比后者長。
            由于Windows下沒有像Linux下的vmstat這樣的工具,這里我們使用Process Explorer看看程序執行的時候線程上線文切換的次數。
            Single Thread:
            
            計數期間,線程總共切換了580-548=32次。(548是啟動程序后,初始的數值)
            Two Thread Switch:
           
            計數期間,線程總共切換了33673295-124=33673171次。(124是啟動程序后,初始的數值)
            Multi Threads - 100 Threads:
            
            計數期間,線程總共切換了846-329=517次。(329是啟動程序后,初始的數值)
            Multi Threads - 2 Threads:
            
            計數期間,線程總共切換了295-201=94次。(201是啟動程序后,初始的數值)
            從上面收集的數據來看,和我們的判斷基本相符。
            干活的其實是CPU,而不是線程
            再想想原來學過的知識,之前一直以為線程多干活就快,簡直是把學過的計算機原理都還給老師了。真正干活的不是線程,而是CPU。線程越多,干活不一定越快。
            那么高并發的情況下什么時候適合單線程,什么時候適合多線程呢?
            適合單線程的場景:單個線程的工作邏輯簡單,而且速度非常快,比如從內存中讀取某個值,或者從Hash表根據key獲得某個value。Redis和Node.js這類程序都是單線程,適合單個線程簡單快速的場景。
            適合多線程的場景:單個線程的工作邏輯復雜,等待時間較長或者需要消耗大量系統運算資源,比如需要從多個遠程服務獲得數據并計算,或者圖像處理。
            例子程序:http://pan.baidu.com/s/1ntNUPWP

          posted on 2014-06-20 11:30 順其自然EVO 閱讀(442) 評論(0)  編輯  收藏 所屬分類: 測試學習專欄性能測試

          <2014年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 宁武县| 西林县| 邵武市| 德化县| 民乐县| 浦城县| 尼玛县| 佛冈县| 景谷| 惠东县| 巴塘县| 青铜峡市| 柳江县| 乐山市| 醴陵市| 丹阳市| 大安市| 印江| 侯马市| 贺兰县| 道孚县| 清远市| 滁州市| 布尔津县| 临武县| 海城市| 和硕县| 郑州市| 邯郸县| 西畴县| 土默特左旗| 绍兴县| 边坝县| 天祝| 康平县| 大庆市| 阳朔县| 罗定市| 金阳县| 靖边县| 乌鲁木齐市|