zoukankan      html  css  js  c++  java
  • 单核CPU,多线程与性能

     

     

    问题概述

    单核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操作的任务, 比如等待用户按键, 读取文件, 网络通讯等, 多线程才是正当其选的解决方案.

  • 相关阅读:
    JID 2.0 RC4 发布,高性能的 Java 序列化库
    FBReaderJ 1.6.3 发布,Android 电子书阅读器
    Arquillian 1.0.3.Final 发布,单元测试框架
    JavaScript 的宏扩展 Sweet.js
    Hypertable 0.9.6.5 发布,分布式数据库
    JRuby 1.7.0 发布,默认使用 Ruby 1.9 模式
    httppp 1.4.0 发布,HTTP响应时间监控
    Redis 2.6.0 正式版发布,高性能K/V服务器
    OfficeFloor 2.5.0 发布,IoC 框架
    XWiki 4.3 首个里程碑发布
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/4988300.html
Copyright © 2011-2022 走看看