线程上下文切换的性能损耗测试

线程上下文切换的性能损耗到底有多少,一直没有直观的理解,今天写个程序测试一下。先看看下面的程序(点击下载):

  

  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

最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-11-16 18:22:35

线程上下文切换的性能损耗测试的相关文章

Web性能压力测试工具之ApacheBench详解

PS:网站性能压力测试是性能调优过程中必不可少的一环.只有让服务器处在高压情况下才能真正体现出各种设置所暴露的问题.Apache中有个自带的,名为ab的程序,可以对Apache或其它类型的服务器进行网站访问压力测试. ApacheBench命令原理: ab命令会创建很多的并发访问线程,模拟多个访问者同时对某一URL地址进行访问.它的测试目标是基于URL的,因此,既可以用来测试Apache的负载压力,也可以测试nginx.lighthttp.tomcat.IIS等其它Web服务器的压力. ab命令

Web服务器性能压力测试工具

Web服务器性能压力测试工具 http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载. 但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死. 还可以测试HTTPS类的网站请求. 下载地址:http_load-12mar2006.tar.gz 安装很简单 tar zxvf http_load-12mar2006.tar.gz cd http_load-12mar2006 make && m

Hadoop参考设计的实现及性能:Hadoop性能初步测试

Name Node/Second Name Node 规格(共两台服务器): DataNode/http://www.aliyun.com/zixun/aggregation/17034.html">TaskTracker 规格: 机柜规格: Hadoop 性能初步测试 基于上述所建立的Hadoop集群,使用标准测试组件进行方案验证,并使用Hadoop性能标杆套件HiBench进行性能测试. nnbench 测试目的:对NameNode的硬件及配置进行负载测试. 参数设置: maps =

无线网络性能监控测试工具iPerf的使用方法

很多公司都在将自己的无线网络升级到802.11n,以实现更大的吞吐量.更广的覆盖范围和更高的可靠性,然而保证无线LAN(WLAN)的性能对于确保足够的网络容量和覆盖率尤为重要.下面,我们将探讨如何通过iPerf来测定网络性能,这是一个简单易用测量TCP/UDP的吞吐量.损耗和延迟的工具. 应用前的准备 iPerf是专门用于简化TCP性能优化的工具,使用它可以很容易地测量吞吐量和带宽的最大值.当与UDP一起使用时,iPerf还可以测量数据丢失和延迟(抖动).iPerf可以在任何IP 网络上运行,包

mysql convert函数性能简单测试

得到了这样一个需求,需要按照拼音字母排序,而mysql数据库使用的是utf编码. 如果使用gbk的话,排序规则是按拼音的. 而mysql中convert函数,可以对数据进行转换. 我们对这个convert进行了简单的性能测试,下面介绍一下测试过程,以及测试结果,如有问题,请各位指出. 软硬件环境 硬件配置:2核CPU.2G内存 数据库:Mysql 5.5 表结构 1 2 3 4 5 CREATE TABLE `test_gbk` ( `id` int(11) NOT NULL AUTO_INCR

HBase写性能初步测试

背景 刚接触HBase,在本机上对线下HBase集群做了初步的写性能测试,下面对测试内容做详细说明. 说明 HBase环境 0.96版本,8台region server,默认配置   写数据说明 单column family,两个column qualifier的值为字符串+随机8位正整数,Row Key为两个quailifer值相连后串上随机Long 比如:val1 = dd1977285, val2 =cc6549921, rowkey = rondom.nextLong() + val1

Nginx 的线程池与性能剖析

正如我们所知,NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求.为此,NGINX工作在非阻塞的socket模式下,并使用了epoll 和 kqueue这样有效的方法. 因为满负载进程的数量很少(通常每核CPU只有一个)而且恒定,所以任务切换只消耗很少的内存,而且不会浪费CPU周期.通过NGINX本身的实例,这种方法的优点已经为众人所知.NGINX可以非常好地处理百万级规模的并

PHP性能监控测试----Xhprof

开始工作到现在,除了做新手任务,基本上都是和服务器端打交道,做前端的时间很短 目前公司的性能监控和测试:Xhprof和ab测试 Xhprof----facebook开源的,轻量级的PHP性能分析工具: 包括函数的调用次数,花费的时间(自身花费时间和包含内部函数花费的时间),所占内存/CPU,所占内存的峰值及所占百分比 具体怎么安装,使用可以去百度一下,这个真的是灰常的好用可以非常快的知道性能瓶颈在哪个文件的哪个函数,然后针对性的做优化:给个截图具体说明性能测试监控工具">数据库,查看源代码

MySQL两种表存储结构MyISAM和InnoDB的性能比较测试

mysql|比较|性能 MySQL支持的两种主要表存储格式MyISAM,InnoDB,上个月做个项目时,先使用了InnoDB,结果速度特别慢,1秒钟只能插入10几条.后来换成MyISAM格式,一秒钟插入上万条.当时决定这两个表的性能也差别太大了吧.后来自己推测,不应该差别这么慢,估计是写的插入语句有问题,决定做个测试:测试环境:Redhat Linux9,4CPU,内存2G,MySQL版本为4.1.6-gamma-standard测试程序:Python+Python-MySQL模块.测试方案:1