问题概述

单核CPU的计算机上, 多线程能够提高程序运行的性能吗? 这个问题看起来简单,实际很复杂,设计到多方面的因素. 首先我们要把概念搞清楚, 那就是什么是性能? 一般来说, 我们把运行一个任务所花的时间来评价性能, 所花的时间可以是在CPU上, 也可能是在I/O操作上, 运行任务的程序, 也可能同时在运行另外若干的任务(吞吐量). 这里我们把概念给缩小一下:

我们这里把性能限制在一个程序运行一个任务, 这个任务是只消耗CPU资源(CPU bound), 所花的时间越小, 说明性能越好. 为了纯粹地说明问题, 我们排除了数据共享问题, 即线程之间不做任何同步动作, 完全隔离.

从理论上说,如果计算机只执行这一个测试程序, 那么单线程要比多线程性能好,因为多线程需要做线程上下文环境的切换; 而当计算机同时运行其他的进程, 假设其他进程里也有多个大量消耗CPU的任务, 那么我们的程序由于是多线程, 抢到CPU时间片的机会增多, 它的性能应该好于单线程.

理论上是这么回事, 但我们知道, 实践与理论是有差距的, 我们的测试不可能在真空环境中. 操作系统的实现有高度的戏剧性, 谁也不能预测实际的测试一定与理论相符, 另外在我们实际的运行环境中,各种情况导致的系统差异很大, 因为我们必须做一些测试.

MSDN上有一篇著名的文章<<Win32 Multithreading Performance>>, 里面讲的东西与我们很相近.它主要论述的是串行计算和并行计算的性能比较, 我们直接拿它的例子, 进行一些更改, 来测试我们的假设.

首先讨论我们测试的主题与它的不同, 主要有2点: 1, 我们讨论的任务是一个固定的任务, 而它里面讨论的是数个计算量不同的任务, 而计算不同的任务涉及到吞吐量的概念; 2, 我们讨论的是线程数量之间的比较(分别测试不同数量的线程), 而它只有单线程与多线程的比较.

图1: 多个不同的任务并行处理

图案2: 一个固定的任务并行处理

测试程序介绍

一个任务在这里简化为一个持续占用CPU的计算, 为了测试的准确性, 中间不可以有任何的I/O操作, 例如:

for (int iCounter=0; iCounter<iLoopCount; iCounter++);

给定的任务量iDelay,单位是毫秒, 有一段模拟的代码, 从而把一秒的时间转换为循环数iLoopCount.里面所用的两个API是QueryPerformanceFrequency和QueryPerformanceCounter.

我们增加一个方法来取代OnWorstcase:

void CThreadlibtestView::OnFixTask()

{

if(!m_iNumberOfThreads)

{

MessageBeep(-1);

return;

};

for (int iLoop=0;iLoop<m_iNumberOfThreads;iLoop++)

{

m_iNumbers[iLoop]=(int)&m_tbConc[iLoop];

m_tbConc[iLoop].iId=iLoop;

if(iLoop==0)

m_tbConc[iLoop].iDelay=m_iDelay/m_iNumberOfThreads+m_iDelay%m_iNumberOfThreads;

else

m_tbConc[iLoop].iDelay=m_iDelay/m_iNumberOfThreads;

m_tbConc[iLoop].tbOutputTarget=this;

m_tbConc[iLoop].iStartOrder=0;

m_tbConc[iLoop].iEndOrder=0;

m_tbConc[iLoop].iTouchCount=0;

};

}

另外我们指定特定的任务量和线程数量:

int iTaskSize[]={100,500,1000,2000,4000};

int iThreadSize[]={2,5,10,15,20};

单线程时候我们调用OnSerial(), 多线程时调用OnConcurrent(), 线程数量分别取iThreadSize里的值.

测试次数仍然保持10次, 取平均值, 整个测试我们分别运行两次, 第一次在负载很轻的计算机上运行, 第二次在同样的计算机上加载一个大负载的进程, 此进程里有十个线程, 每个线程都是基于CPU的密集计算, 程序如下:

DWORD WINAPI ThreadFunc(LPVOID lpParam)

{

DWORD busyTime=10;

while(true)

{

DWORD startTime=GetTickCount();

for(;GetTickCount()-startTime<=busyTime;)

;

Sleep(1);

}

return 0;

}

特别要注意的是, 两次计算不能各启动一个进程, 必须在一个进程中, 因为在程序开始,我们会算出1秒对应循环数, 而每次启动程序这个数字是不同的, 为了更有意义的比较,我们要求这个数字一定相同.

测试环境: CPU Intel Pentium M 1.7 G; 1047472KB RAM, Windows 2000 professional SP4.

一秒的循环数:146278208

测试结果

图3: 在负载轻的系统上测试结果

图4: 在负载重的系统上测试结果

结论和建议

根据测试结果,我们可以得出一些结论(针对单核CPU):

在负载轻的系统上, 多线程不适合处理基于CPU的任务,而在负载重的系统上,多线程可以帮助提高性能.

这是一个模糊和慎重的结论, 因为测试结果里的一些现象我也给不出合适的理由, 应该属于操作系统”戏剧化”和”不确定性”的表现吧(操作系统会对某些线程动态提高优先级,但本例中的线程优先级保持不变.), 例如在负载轻的机器上, 20个线程和2个线程运行时间差异不大; 在负载重的机器上, 2个线程运行时间比单线程还长, 而当线程数量增加到5时, 性能才有显著的提高.

所以我的建议是,在单核CPU机器上,处理CPU密集的任务时, 不推荐使用多线程, 除非你对目标机器非常的了解和确定,并经过严格的测试. 当然, 涉及到其他I/O操作的任务, 比如等待用户按键, 读取文件, 网络通讯等, 多线程才是正当其选的解决方案.

单核CPU,多线程与性能的更多相关文章

  1. 单核cpu多线程有必要吗?

    问题分析 现代计算机一般都是多核cpu,多线程的可以大大提高效率,但是可能会有疑问,那单核CPU使用多线程是不是没有必要了,假定一种情况,web应用服务器,单核CPU.单线程,用户发过来请求,单个线程 ...

  2. 多线程程序在单核cpu与多核cpu上是怎么工作的?

    转自 1.多线程在单核和多核CPU上的执行效率问题的讨论 a1: 多线程在单cpu中其实也是顺序执行的,不过系统可以帮你切换那个执行而已,其实并没有快(反而慢) 多个cpu的话就可以在两个cpu中同时 ...

  3. 对于多线程程序,单核cpu与多核cpu是怎么工作的

    此文中的大部分资料来自于网络上,我只是觉得把有道理的整理一下,方便以后查阅. 1.多线程在单核和多核CPU上的执行效率问题的讨论a1: 多线程在单cpu中其实也是顺序执行的,不过系统可以帮你切换那个执 ...

  4. “多个单核CPU”与“单个多核CPU”哪种方式性能较强?

    多个单核CPU: 成本更高,因为每个CPU都需要一定的线路电路支持,这样对主板上布局布线极为不便.并且当运行多线程任务时,多线程间通信协同合作也是一个问题.依赖总线的传输,速度较慢,且每一个线程因为运 ...

  5. 单核CPU并发与非并发测试

    多线程运行程序的目的一般是提高程序运行效率并且能够提高硬件的利用率比如多核CPU,但是如果我们只有单核CPU并发运行程序会怎样呢? 我以两个环境作为对比: 环境A(我本机8c) 环境B(我的云服务器1 ...

  6. 单核、多线程与时间片,以Node.js为例

    去年写了篇文章<线程与进程的理解>,当时认为线程和CPU的单核多核无关,操作系统可以有很多个线程.但今天看<深入浅出Node.js>一书时,多次提到单线程无法利用多核CPU一类 ...

  7. 原来 CPU 为程序性能优化做了这么多

    本文主要来学习内存屏障和 CPU 缓存知识,以便于我们去了解 CPU 对程序性能优化做了哪些努力. 首先来看下 CPU 缓存: CPU 缓存 CPU 缓存是为了提高程序运行的性能,CPU 在很多处理上 ...

  8. zabbix监控单核cpu使用率和多核cpu总负载

    zabbix自带的基础监控的模板中只有对单核cpu负载1分钟.5分钟.15分钟的监控. 添加对总的cpu负载的监控 key:system.cpu.load[all,avg1] 1分钟cpu总的负载 添 ...

  9. ixgbe 82599 固定源与目标, UDP, 64字节小包, 1488w pps 单核CPU软中断sirq 100%

    ixgbe 82599 固定源与目标, UDP, 64字节小包, 1488w pps 单核CPU软中断sirq 100% 注: 测试使用, 正常应用不要开启 五元组不同, 开启ntupleethtoo ...

随机推荐

  1. SSO(转)

    一.介绍       主站下有多个子系统,每次登录主系统,跳转到子系统时,又需要重新登录: 子系统与主系统都有各自的用户信息表:各个系统的用户角色.权限也各不相同: 二.目的       每次登录主系 ...

  2. Spring AOP实现方式三【附源码】

    注解AOP实现 源码结构: 1.首先我们新建一个接口,love 谈恋爱接口. package com.spring.aop; /** * 谈恋爱接口 * * @author Administrator ...

  3. 【HDOJ】1050 Moving Tables

    贪心问题,其实我觉得贪心就是合理的考虑最优情况,证明贪心可行即可.这题目没话多久一次ac.这道题需要注意房间号的奇偶性.1 3.2 4的测试数据.答案应该为20. #include <stdio ...

  4. bzoj2938

    显然AC自动机,但什么叫无限生成呢?显然就是在AC自动机上匹配,出现了一个环(不能走结尾节点)直接搜索即可 ..,'] of longint; q,f:..] of longint; can,v,r: ...

  5. bzoj1922

    首先机器人是并行的: 很容易想到到某个点的最短用时 =max(到这个点的最短路,max(到保护这个点结界所在点的最短用时)) 所以我们在做dij的时候,d[j]维护最短路,w[j]维护所有保护这个点结 ...

  6. Azure 媒体服务发布可靠的视频直播平台

    Sudheer Sirivara Azure 媒体服务总监 两个月前,Azure 媒体服务发布了视频直播和内容保护产品的公共预览版.这一 Internet规模的直播解决方案已被十余家业界领先的国际广播 ...

  7. winhex的使用

    1. 当我们用VC编写代码,将数据写入到磁盘的文件后,用winhex查看,winhex可以检测文件最后写入时间的变动,如果有变动,则会提示“此文件的最后写入时间已经改变.解除当前状态并重新载入吗?”, ...

  8. windows查看服务端口

    开始--运行--cmd 进入命令提示符 输入netstat -ano 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选&qu ...

  9. android webview 访问https页面 SslError 处理

    在Android中,WebView可以用来加载http和https网页到本地应用的控件.但是在默认情况下,通过loadUrl(String url)方法,可以顺利loadUrl(“http://www ...

  10. Yet Another Scheme Introduction学习

    Chapter 2 Function exact->inexact is to convert from fractional numbers to floating point numbers ...