打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
1.1

让多核CPU占用率曲线听你指挥——《编程之美》1.1学习笔记

Problem:   

写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。有以下几种情况:

1.CPU占用率固定在50%,为一条直线;

2.CPU的占用率为一条直线,具体占用率由命令行参数决定(范围1~100);

3.CPU的占用率状态为一条正弦曲线。

分析与解法:

(1)通过观察任务管理器,它大约1s更新一次。当CPU使用率为0时,System Idle Process占用了CPU的空闲时间。

System Idle Process在CPU空闲的的时候,发出一个IDLE命令,使CPU挂起(暂时停止工作),可有效的降低CPU内核的温度,无法终止。在这个进程里出现的CPU占用数值并不是真正的占用而是体现的CPU的空闲率,也就说这个数值越大CPU的空闲率就越高,反之就是CPU的占用率越高。Linux中对应的进程为init,PID为1。

 

当系统中的进程或者在等待用户输入,或者在等待某些事件的发生(发出I/O请求等待I/O响应),或者主动进入休眠状态(比如Sleep())。

在任务管理器中的一个刷新周期内,CPU忙(执行应用程序)的时间和刷新周期总时间的比率就是CPU的占用率。其显示的是每个刷新周期内CPU占用率的统计平均值。我们可以写一个程序让它在任务管理器的刷新时间内一会儿忙,一会儿闲,通过调节忙/闲的比例,来控制任务管理器中显示的CPU占用率。

 

书上的代码以单核CPU为前提,但对于多核CPU来说,同一个进程可能被CPU的任务分配器分配到不同的核心上执行,所以造成无法让任务管理器达到预想的效果。其实打开任务管理器,可以看到多个CPU使用记录。本人电脑CPU是Core i5 450M,双核4线程。在OS看来就如同有四个CPU工作一样。我的任务管理器中就有四个CPU使用记录。

所谓超线程技术就是利用特殊的硬件指令,把多线程处理器内部的两个逻辑内核模拟成两个物理芯片,从而使单个处理器就能“享用”线程级的并行计算的处理器技术。多线程技术可以在支持多线程的操作系统和软件上,有效的增强处理器在多任务、多线程处理上的处理能力。

 

可以使用SetProcessAffinityMask()函数可以使特定的处理器运行指定进程。

BOOL SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask);

第一个参数用来指定指定哪个进程,传入它的句柄。第二个进程用来指定哪个CPU核心来执行此进程。

DWORD_PTR,其实就是unsigned long*.Unsigned long type for pointer precision.Use when casting a pointer to a long type to perform pointer arithmetic.(Also commonly used for general 32-bit parameters that have been extended to 64 bits in 64-bit windows.)

DWORD 其实就是unsigned long。Windows下常用来保存地址或存放指针。

比如这样调用函数:

::SetProcessAffinityMask(::GetCurrentProcess(),0x1);可以指定当前执行的进程在第一个CPU上运行(00000001)。对于双核CPU,

::SetProcessAffinityMask(::GetCurrentProcess(),0x2);可以指定在第二个CPU上运行。(00000010)

::SetProcessAffinityMask(::GetCurrentProcess(),0x3);可以允许在两个CPU上任意运行。(000000011)

        ::SetProcessAffinityMask(::GetCurrentProcess(),0x3);可以允许在第三个CPU上任意运行。(000000100)

       ::SetProcessAffinityMask(::GetCurrentProcess(),0x3);可以允许在第一个和第三个CPU上任意运行。(00000101)

 

        以此类推。。。

 

 

HANDLE GetCurrentProcess(void);

可以获得当前进程的句柄。注意,这个句柄为一个伪句柄。只能在我们的进程中才能代表当前进程的句柄,事实上这个函数目前只是简单的返回-1这个值。也就是说在我们的程序中-1便能表示本进程的句柄。

(2)那么对于绘制50%直线,程序代码为:

 

 

  1. #include <Windows.h>   
  2. #include<stdlib.h>   
  • #include<tchar.h>   
  • int _tmain(int argc,_TCHAR* argv[])  
  • {  
  •     int busyTime = 10;  
  •     int idleTime = busyTime*5;  
  •     __int64 startTime = 0;  
  •     ::SetThreadAffinityMask(::GetCurrentProcess(),0x00000001);  
  •     while(true)  
  •     {  
  •         startTime = GetTickCount();  
  •                 //busy loop   
  •         while((GetTickCount() - startTime) <= busyTime);  
  •                 //idle loop   
  •             Sleep(idleTime);  
  •     }  
  •     return 0;  
  • }  
  • GetTickCount()可以得到系统从启动到运行到现在所经历时间的毫秒值。最多能统计到49.7天。我们利用它判断busy loop要持续多久。

    其中idleTime为busyTime的五倍,可以修改其值使其更逼近50%。不同机子的情况不同。

    __int64是VC++的64位扩展。范围为[-2^63,2^63)。当64位与32位混合运算时,32位整数会隐式转换成64位整数。输入输出它时使用cin、cout会造成错误。需要使用scanf("%I64d",&a);和printf("%I64d",a);

    还有unsigned __int64,其范围为[0,2^64)。

    对应g++中的64位扩展为long long和unsigned long long。范围与运算与上相仿。输入输出使用scanf("%lld",&a);和printf("%lld",a);

     

    int _tmain(int argc, _TCHAR* argv[])。

    _tmain这个符号多见于VC++创建的控制台工程中,这个是为了保证移植unicode而加入的(一般_t、_T、T()这些东西都和unicode有关系)。定义在头文件tchar.h中。

    (3)对于绘制正弦曲线:

     

    1. #include <Windows.h>   
    2. #include<stdlib.h>   
    3. #include<math.h>   
    4. #include<tchar.h>   
    5. const double SPLIT = 0.01;  
    6. const int COUNT = 200;  
    7. const double PI = 3.14159265;  
    8. const int INTERVAL = 300;  
    9. int _tmain(int argc, _TCHAR* argv[] )  
    10. {  
    11.     DWORD busySpan[COUNT]; //array of busy times   
    12.     DWORD idleSpan[COUNT]; //array of idle times   
    13.     int half = INTERVAL/2;  
    14.     double radian = 0.0;  
    15.         //如何近似趋近一条正弦曲线?这样!   
    16.     for(int i = 0; i < COUNT; ++i)  
    17.     {  
    18.         busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));  
    19.         idleSpan[i] = INTERVAL - busySpan[i];  
    20.         radian += SPLIT;  
    21.     }  
    22.     DWORD startTime = 0;  
    23.     int j = 0;  
    24.     ::SetProcessAffinityMask(::GetCurrentProcess(),0x00000002);   
    25.     while(true)  
    26.     {  
    27.         j = j % COUNT;  
    28.         startTime = GetTickCount();  
    29.         while((GetTickCount() - startTime) <= busySpan[j]);  
    30.             Sleep(idleSpan[j]);  
    31.         j++;  
    32.     }  
    33.     return 0;  
    34. }  

    通过在一个周期2*PI中等分200份,将每一个间隔点的half + (sin( PI * radian) * half))的值存入busySpan[i],将其补植存入idleSpan[i]。half是整个值域INTERVAL的一半。这样可以近似趋近一条正弦曲线。

    运行效果为:

    (4)可以通过RDTSC指令获得当前CPU核心运行周期数。

    在x86平台上定义函数:

    1. inline __int64 GetCPUTickCount()  
    2. {  
    3.     __asm  
    4.     {  
    5.         rdtsc;  
    6.     }  
    7. }  

    在x64平台上定义:

    1. #define GetCPUTickCount() __rdtsc()  

    使用CallNtPowerInformation API得到CPU频率,从而将周期数转化为毫秒数,例如如下:

    1. _PROCESSOR_POWER_INFORMATION info;  
    2. CallNTPowerInformation(11,              //query processor power information   
    3.     NULL,                               //no input buffer   
    4.     0,                                  //input buffer size is zero   
    5.     &info,                              //output buffer   
    6.     sizeof(info)                        //outbuf size   
    7.     );  
    8. __int64 t_begin = GetCPUTickCount();  
    9. //do something   
    10. __int64 t_end = GetCPUTickCount();  
    11. millisec = ((double)t_end - (double)t_begin)/(double)info.CurrentMhz;  
     

     

    RDTSC指令读取当前CPU的周期数,在多CPU系统中这个周期数在不同的CPU间基数不同,频率也不同。用从两个不同的CPU得到的周期数来计算会得出没有意义的值。所以需要用SetProcessAffinityMask避免进程迁移。另外,CPU的频率也会随系统供电及负荷情况有所调整。
    本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
    打开APP,阅读全文并永久保存 查看更多类似文章
    猜你喜欢
    类似文章
    【热】打开小程序,算一算2024你的财运
    让cpu占用率曲线听你指挥学习笔记
    编程之美,JAVA控制CPU的使用率(2),完美曲线
    编程之美 1.1 让cpu占用率曲线听你指挥(多核处理器)
    写程序 控制cpu占用率-4 绘制CPU使用率的正弦曲线2 通过C#指定线程运行的 CPU
    分享一个在内存里搜索QQ号码的源码,源自看雪论坛
    windows 的数据类型
    更多类似文章 >>
    生活服务
    热点新闻
    分享 收藏 导长图 关注 下载文章
    绑定账号成功
    后续可登录账号畅享VIP特权!
    如果VIP功能使用有故障,
    可点击这里联系客服!

    联系客服