问题概述

单核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. VC C运行时库(CRTL)的几个版本及选用

    分类: Windows 2008-12-23 10:01 987人阅读 评论(0) 收藏 举报ciostreammfclibrary多线程import最近做项目碰到了一个关于在动态库中使用MFC以及在 ...

  2. ADDED_TO_STAGE 多次被调用

    var mca:MovieClip=new MCA(); mca.addEventListener(Event.ADDED_TO_STAGE,addStageEvent); function addS ...

  3. jQuery的几个Grid插件简单比较

    目标:实现一个类似于Excel功能的Grid数据维护功能,并且就地编辑在乎的是Cell编辑而不是行编辑 候选者:easy-ui之datagrid, jqgrid, flexigrid 使用环境:jqu ...

  4. sql - and - or

    sql - and SQL AND links together two or more conditional statements for increased filtering when run ...

  5. 转载-smarty教程(基本语法)

    转自:http://hi.baidu.com/qxxgvpdtzhbckpr/item/681049160d7be60db98a1aec 1.smarty的配置      首先,使用smarty第一件 ...

  6. Growling Gears

    http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11587 G Growling GearsThe Best A ...

  7. [TOP]疯狂的投资

    [TOP]疯狂的投资 这是罗辑思维一期<疯狂的投资>的节目笔记,这期主要是通过菲尔德铺设横跨大西洋的电缆的故事讲了创业者需要的特质和<二十一世纪资本论>的一个观念:随着自由市场 ...

  8. cefSharp 设置运行时系统语言

    在使用用CefSharp使用过程中,系统用了很多第三方控件.这些控件很多能够根据浏览器设置的语言来进行控件展示对应语言. 在cefSharp可以设置系统语言,代码如下: CefSharp.Settin ...

  9. 初学redux笔记,及一个最简单的redux实例

    categories: 笔记 tags: react redux 前端框架 把初学redux的一些笔记写了下来 分享一个入学redux很合适的demo, 用redux实现计数器 这是从阮一峰老师git ...

  10. curl检查远程文件是否存在

    size_t processdata(void *buffer, size_t size, size_t nmemb, void *user_p) { //写一个空的写函数 return nmemb; ...