• 编程之美读书笔记1.1——让CPU占用率曲线听你的指挥


    http://blog.csdn.net/pipisorry/article/details/36189155

    
    
    <strong><span style="font-size:48px;color:#ff0000;">问题:</span></strong>写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。

    假设机器是多CPU,上面的程序会出现什么结果?怎样在多个CPU时显示相同的状态?比如。在双核的机器上,假设让一个单线程的程序死循环,能让两个CPU的使用率达到50%的水平么?为什么?多CPU的问题首先须要获得系统的CPU信息。能够使用GetProcessorInfo()获得多处理器的信息,然后指定进程在哪一个处理器上执行。

    当中指定执行使用的是SetThreadAffinityMask()函数。

    书中提到两个重要的API,GetProcessorInfo()与SetThreadAffinityMask()。经查询MSDN。GetProcessorInfo()是Windows Server上的API,在PC机上不好用。所以能够尝试GetSystemInfo()来获得核数。

    实际上在Windows平台上直接用任务管理器就能够查看。任务管理器上会用四个窗体来显示CPU的使用情况(Windows7 酷睿i5-3230M)


    四核都显示正弦曲线算法:

    參考代码:

    /*********************************************************************************************/
    /* 控制cpu占用率曲线(50%直线、1~100随意直线、正弦曲线)皮皮 2014-6-30 */
    /********************************************************************************************/
    #include <Windows.h>
    #include <stdio.h>
    #include <math.h>
    using namespace std;
    
    const int SAMPLE_NUM = 200;
    DWORD busy_time[200];
    const DWORD TOTAL_TIME_SLICE = 100; //每隔100ms取下一个採样点。让cpu工作在相应振幅的时间
    const DWORD HALF_TOTAL_TIME_SLICE = TOTAL_TIME_SLICE/2;
    //sine 曲线
    void sine(){
    DWORD pre_time;
    for(int i = 0 ; ; i = (i+1)%SAMPLE_NUM ){
    pre_time = GetTickCount();
    while( (GetTickCount() - pre_time) <= busy_time[i]);//cpu忙
    Sleep(TOTAL_TIME_SLICE - busy_time[i]);//cpu闲
    }
    }
    
    
    const double PI_L = 0.031415926535;
    //对sine採样
    void sine_sampling(){
    for(int i = 0 ; i < SAMPLE_NUM ; i++){
    busy_time[i] = (DWORD)( sin( PI_L*i )*HALF_TOTAL_TIME_SLICE + HALF_TOTAL_TIME_SLICE );//busy_time值范围 (-1~1) *50+50 = (0~100)
    //printf("busy_time[%d] = %d
    ",i,busy_time[i]);
    }
    }
    
    
    void main(){
    sine_sampling();
    
    //printf("CurrentProcessorNumber : %d
    ",(int)GetCurrentProcessorNumber());
    SYSTEM_INFO info;
        GetSystemInfo(&info); //调用API函数来获取计算机硬件的信息
    printf("dwNumberOfProcessors : %d
    ",info.dwNumberOfProcessors);//cpu核数
    
    DWORD dwThreadId;
    HANDLE hThread;
    int coreNum = info.dwNumberOfProcessors;
    for(int i = 0 ; i < coreNum ; i ++){
    hThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)sine,0,0,&dwThreadId);
    SetThreadAffinityMask(hThread,1<<i);//针对cpu核i(from 0)
    }
        WaitForSingleObject(hThread, INFINITE); //传入INFINITE表示无限等待
    }


    代码部分解释:

    1.CreateThread

    函数功能:创建线程

    函数原型:

    CreateThread(
        __in_opt  LPSECURITY_ATTRIBUTES lpThreadAttributes,
        __in      SIZE_T dwStackSize,
        __in      LPTHREAD_START_ROUTINE lpStartAddress,
        __in_opt __deref __drv_aliasesMem LPVOID lpParameter,
        __in      DWORD dwCreationFlags,
        __out_opt LPDWORD lpThreadId
        );

    函数说明:

    第一个參数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。

    第二个參数表示线程栈空间大小。

    传入0表示使用默认大小(1MB)。

    第三个參数表示新线程所运行的线程函数地址,多个线程能够使用同一个函数地址。

    第四个參数是传给线程函数的參数。

    第五个參数指定额外的标志来控制线程的创建,为0表示线程创建之后马上就能够进行调度。假设为CREATE_SUSPENDED则表示线程创建后暂停执行。这样它就无法调度,直到调用ResumeThread()

    第六个參数将返回线程的ID号,传入NULL表示不须要返回该线程ID号。

    函数返回值:成功返回新线程的句柄。失败返回NULL 


     2.WaitForSingleObject

    函数功能:等待函数 – 使线程进入等待状态。直到指定的内核对象被触发。

    函数原形:

    WaitForSingleObject(
        __in HANDLE hHandle,
        __in DWORD dwMilliseconds
        );

    函数说明:

    第一个參数为要等待的内核对象。

    第二个參数为最长等待的时间,以毫秒为单位,如传入5000就表示5秒。传入0就马上返回。传入INFINITE表示无限等待。

    由于线程的句柄在线程执行时是未触发的。线程结束执行。句柄处于触发状态。所以能够用WaitForSingleObject()来等待一个线程结束执行。

    函数返回值:在指定的时间内对象被触发。函数返回WAIT_OBJECT_0。超过最长等待时间对象仍未被触发返回WAIT_TIMEOUT。传入參数有错误将返回WAIT_FAILED


    3.SetThreadAffinityMask()

    函数功能:Sets a processor affinity mask for the specified thread.

    函数原形:

    WaitForSingleObject(
        __in HANDLE hHandle,
        __in DWORD dwMilliseconds
        );

    函数说明:

    这个函数的參数有两个,第一个是运行任务的句柄,第二个是核的掩蔽码。

    掩蔽码的使用规则是假设最低位置1,就将任务分派给编号为0的核,假设次低位置1,就将任务分派给编号为1的核。

    假设掩蔽码设置为0xC。即编号为2, 3的核被置为1,线程就会在这两个核上切换。

    more:

    进程与指定cpu绑定:SetProcessAffinityMask(GetCurrentProcess(), dwMask);

    线程与指定cpu绑定:SetThreadAffinityMask(GetCurrentThread(),dwMask);

    dwMask为CPU序号的或运算值:1(0001)代表仅仅执行在CPU1,2(0010)代表仅仅执行在CPU2。3(0011)代表能够执行在CPU1和CPU2,以此类推。

    ref: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686247(v=vs.85).aspx


    结果截图:





    附:

    1.TOTAL_TIME_SLICE 的计算

    假设不考虑其他程序的CPU占用情况,能够在每一个核上开一个线程。执行指定的函数,实现每一个核的CPU占用率同样。 

    要让CPU的占用率。呈函数 y = calc(t) (0 <= y <= 1, t为时间,单位为ms )分布。仅仅要取间隔非常短的一系列点。觉得在某个间隔内。y值近似不变。

    设间隔值为GAP。显然在指定t值附近的GAP这段时间内,

    CPU占用时间为:busy = GAP * calc(t)。

    CPU空暇时间为:idle = GAP – busy

    因此,非常easy写出以下这个通用函数: 

    void solve(Func *calc)
    {
      
    double tb = 0;
      
    while(1{
        unsigned ta 
    = get_time();
        
    double r = calc(tb);
        
    if (r < 0 || r > 1) r = 1;
        DWORD busy 
    = r * GAP;
        
    while(get_time() - ta < busy) {}
        Sleep(GAP 
    - busy);
        tb 
    += GAP;
      }

    }

    假设CPU占用率曲线不是周期性变化,就要对每一个t值都要计算一次,否则。能够仅仅计算第一个周期内的各个t值,其他周期的直接取缓存计算结果。

     以CPU占用率为正弦曲线为例,显然:y = 0.5 * (1 + sin(a * t + b))

    其周期T = 2 * PI / a  (PI = 3.1415927),能够指定T值为60s即60000ms。则

     能够确定a值为 2 * PI / T, 若在这60000ms内我们计算200次(c = 200),则GAP值为 T / c = 300ms.也就是说,仅仅要确定了周期和计算次数,其他几个參数也都确定下来。


    2.CPU亲缘性(Affinity)介绍

    依照默认设置。当系统将线程分配给处理器时,Windows使用软亲缘性来进行操作。这意味着假设全部其它因素同样的话,它将设法在它上次执行的那个处理器上执行线程。让线程留在单个处理器上。有助于反复使用仍然在处理器的内存快速缓存中的数据。

    有一种新的计算机结构,称为NUMA(非统一内存訪问),在该结构中,计算机包括若干块插件板,每一个插 件板上有4个CPU和它自己的内存区。
    当CPU訪问的内存是它自己的插件板上的内存时,NUMA系统执行的性能最好。假设CPU须要訪问位于还有一个插件板上的内 存时,就会产生巨大的性能减少。在这种环境中,就须要限制来自一个进程中的线程在共享同一个插件版的CPU上执行。为了适应这种计算机结构的须要。Windows同意你设置进程和线程的亲缘性。

    换句话说。你能够控制哪个CPU能够执行某些线程。

    这称为硬亲缘性。

    请注意。子进程能够继承进程的亲缘性。

    注意:

    (1)不管计算机中实际拥有多少个CPU。Windows98及曾经系统仅仅使用一个CPU,上述API不被支持。

    (2)在大多数环境中,改变线程的亲缘性就会影响调度程序有效地在 各个CPU之间移植线程的能力,而这样的能力能够最有效地使用CPU时间。


    3.CreateThread()函数

    它是Windows提供的API接口,在C/C++语言另有一个创建线程的函数_beginthreadex(),在非常多书上(包含《Windows核心编程》)提到过尽量使用_beginthreadex()来取代使用CreateThread()。这是为什么?http://blog.csdn.net/morewindows/article/details/7421759


    ref:

    http://blog.csdn.net/pipisorry/article/details/36189155

    http://blog.csdn.net/morewindows/article/details/7421759

    http://www.cnblogs.com/foreverinside/p/3577273.html

    http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991965.html

    http://blog.csdn.net/u012370255/article/details/28232153

  • 相关阅读:
    raspberry pi (树莓PI)使用socket向PC发送视频流
    树莓PI交叉编译BOOST库(asio网络例子)
    远程调试树莓PI
    opencv车速检测
    屏蔽同步(JAVA)
    linux常用命令记录
    虚拟机中Ubuntu设置固定IP方法
    java笔记01-反射
    java笔记00-目录
    mysql由于外键关联无法删除数据
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/6718096.html
Copyright © 2020-2023  润新知