• 编程之美 1.1 让cpu占用率曲线听你指挥(多核处理器)


    【目录】

    不考虑其他进程,cpu画正弦曲线

    获取总体cpu利用率

    获取多核处理器单个cpu利用率

    考虑其他进程,cpu画正弦曲线


    下面的程序针对多核处理器,可以设置让任何一个cpu显示相应的曲线(本文以正弦曲线为例)

    代码编译环境:windows 7 64位 酷睿 i5 处理器,vs2010.

    可以修改CpuSin函数的计算 busySpan 和 idleSpan的部分以显示不同的曲线。

    下面的代码没有考虑cpu中其他进程的占用情况,这种情况详见第二部分

     1 #include <windows.h>
     2 #include <stdio.h>
     3 #include <math.h>
     4 
     5 //把一条正弦曲线0~2pi 之间的弧度等分200份抽样,计算每个点的振幅
     6 //然后每隔300ms设置下一个抽样点,并让cpu工作对应振幅时间
     7 const int samplingCount = 200; //抽样点数目
     8 const double pi = 3.1415926;
     9 const int totalAmplitude = 300; //每个抽样点对应时间片
    10 const double delta = 2.0/samplingCount;  //抽样弧度的增量
    11 
    12 int busySpan[samplingCount];//每个抽样点对应的busy时间
    13 int idleSpan[samplingCount];//每个抽样点对应的idle时间
    14 
    15 //一个线程调用MakeUsageSin,并把该线程绑定到一个cpu,那么该cpu呈现正弦曲线
    16 DWORD WINAPI MakeUsageSin(LPVOID lpParameter)
    17 {
    18     DWORD startTime = 0;
    19     for(int j = 0; ; j = (j + 1) % samplingCount)
    20     {
    21         startTime = GetTickCount();
    22         while ((GetTickCount() - startTime) < busySpan[j]);
    23         Sleep(idleSpan[j]);
    24     }
    25 }
    26 
    27 //如果cpuindex < 0 则所有cpu都显示正弦曲线
    28 //否则只有第 cpuindex个cpu显示正弦曲线
    29 //cpuindex 从 0 开始计数
    30 void CpuSin(int cpuIndex)
    31 {
    32     //计算 busySpan 和 idleSpan
    33     double radian = 0;
    34     int amplitude = totalAmplitude / 2;
    35     for (int i = 0; i < samplingCount; i++)
    36     {
    37         busySpan[i] = (DWORD)(amplitude + sin(pi*radian)*amplitude);
    38         idleSpan[i] = totalAmplitude - busySpan[i];
    39         radian += delta;
    40     }
    41 
    42     //获取系统cup数量
    43     SYSTEM_INFO SysInfo;
    44     GetSystemInfo(&SysInfo);
    45     int num_processors = SysInfo.dwNumberOfProcessors;
    46     if(cpuIndex + 1 > num_processors)
    47     {
    48         printf("error: the index of cpu is out of boundary
    ");
    49         printf("cpu number: %d
    ", num_processors);
    50         printf("your index: %d
    ", cpuIndex);
    51         printf("** tip: the index of cpu start from 0 **
    ");
    52         return;
    53     }
    54 
    55     if(cpuIndex < 0)
    56     {
    57         HANDLE* threads = new HANDLE[num_processors];
    58         for (int i = 0;i < num_processors;i++)
    59         {
    60             DWORD mask = 1<<i;
    61             threads[i] = CreateThread(NULL, 0, MakeUsageSin, &mask, 0, NULL);
    62             SetThreadAffinityMask(threads[i], 1<<i);//线程指定在某个cpu运行
    63         }
    64         WaitForMultipleObjects(num_processors, threads, TRUE, INFINITE);
    65     }
    66     else
    67     {
    68         HANDLE thread;
    69         DWORD mask = 1;
    70         thread = CreateThread(NULL, 0, MakeUsageSin, &mask, 0, NULL);
    71         SetThreadAffinityMask(thread, 1<<cpuIndex);
    72         WaitForSingleObject(thread,INFINITE);
    73     }
    74 
    75 }
    76 int main()
    77 {
    78 
    79     CpuSin(0);
    80     return 0;
    81 }

    运行结果:


    下面我们考虑其他进程对cpu的影响,即需要检测出某个cpu当前的使用率。

    首先对于单核处理器获取cpu利用率,或者多核处理器获取总的cpu利用率,可以通过windows api “GetSystemTimes” 来实现

    该函数声明如下:BOOL GetSystemTimes(LPFILETIME  IdleTime,LPFILETIME   KernelTime,LPFILETIME   UserTime),具体可以参考msdn接口介绍

    cpu利用率计算公式为:CPURate=100.0-(NowIdleTime-LastIdleTime)/(NowKernelTime-LastKernelTime+NowUserTime-LastUserTime)*100.0

    计算总的cpu利用率或者单核处理器cpu利用率的类实现如下:

     1 class CCPUUseRate
     2 {
     3 public:
     4     BOOL Initialize() 
     5     {
     6         FILETIME ftIdle, ftKernel, ftUser;
     7         BOOL flag = FALSE;
     8         if (flag = GetSystemTimes(&ftIdle, &ftKernel, &ftUser))
     9         {
    10             m_fOldCPUIdleTime = FileTimeToDouble(ftIdle);
    11             m_fOldCPUKernelTime = FileTimeToDouble(ftKernel);
    12             m_fOldCPUUserTime = FileTimeToDouble(ftUser);
    13 
    14         }
    15         return flag;
    16     }
    17     //调用Initialize后要等待1左右秒再调用此函数
    18     int GetCPUUseRate()
    19     {
    20         int nCPUUseRate = -1;
    21         FILETIME ftIdle, ftKernel, ftUser;
    22         if (GetSystemTimes(&ftIdle, &ftKernel, &ftUser))
    23         {
    24             double fCPUIdleTime = FileTimeToDouble(ftIdle);
    25             double fCPUKernelTime = FileTimeToDouble(ftKernel);
    26             double fCPUUserTime = FileTimeToDouble(ftUser);
    27             nCPUUseRate= (int)(100.0 - (fCPUIdleTime - m_fOldCPUIdleTime) 
    28                 / (fCPUKernelTime - m_fOldCPUKernelTime + fCPUUserTime - m_fOldCPUUserTime) 
    29                 *100.0);
    30             m_fOldCPUIdleTime = fCPUIdleTime;
    31             m_fOldCPUKernelTime = fCPUKernelTime;
    32             m_fOldCPUUserTime = fCPUUserTime;
    33         }
    34         return nCPUUseRate;
    35     }
    36 private:
    37     double FileTimeToDouble(FILETIME &filetime)
    38     {
    39         return (double)(filetime.dwHighDateTime * 4.294967296E9) + (double)filetime.dwLowDateTime;
    40     }
    41 private:
    42     double m_fOldCPUIdleTime;
    43     double m_fOldCPUKernelTime;
    44     double m_fOldCPUUserTime;
    45 };

    注意:前后两次调用GetSystemTimes之间要间隔一定时间,使用方法如下:

     1 int main()
     2 {
     3     CCPUUseRate cpuUseRate;
     4     if (!cpuUseRate.Initialize())
     5     {
     6         printf("Error! %d
    ", GetLastError());
     7         getch();
     8         return -1;
     9     }
    10     else
    11     {
    12         while (true)
    13         {    
    14             Sleep(1000);
    15             printf("
    当前CPU使用率为:%4d%%", cpuUseRate.GetCPUUseRate());
    16         }
    17     }
    18     return 0;
    19 }

    对于计算多核处理器中单个cpu的使用率,可以使用pdh.h头文件中的接口,该头文件是visual studio自带的,包含该头文件时,还需要引入相关的lib库:

     1 #include <TCHAR.h>
     2 #include <windows.h>
     3 #include <pdh.h>
     4 #include <cstdio>
     5 #include <cmath>
     6 #pragma comment(lib, "pdh.lib")
     7 
     8 //---------------------------------------------------comput the cpu usage rate
     9 static PDH_HQUERY cpuQuery;
    10 static PDH_HCOUNTER cpuTotal;
    11 
    12 //cpuindex 为指定的cpu id ,从0开始
    13 void init(int cpuIndex)
    14 {
    15     PDH_STATUS Status = PdhOpenQuery(NULL, NULL, &cpuQuery);
    16     if (Status != ERROR_SUCCESS) 
    17     {
    18         printf("
    PdhOpenQuery failed with status 0x%x.", Status);
    19         exit(-1);
    20     }
    21     char buf[50];
    22     sprintf(buf, "\Processor(%d)\%% Processor Time", cpuIndex);
    23     PdhAddCounter(cpuQuery, LPCSTR(buf), NULL, &cpuTotal);
    24     PdhCollectQueryData(cpuQuery);
    25 }
    26 
    27 
    28 double getCpuUsageRate()
    29 {
    30     PDH_FMT_COUNTERVALUE counterVal;
    31     PdhCollectQueryData(cpuQuery);
    32     PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
    33     return counterVal.doubleValue;
    34 }

    注:该方法也可以计算总的cpu利用率,只要把PdhAddCounter的第二个字符串参数改为"\Processor(_Total)\%% Processor Time"

          前后两次调用PdhCollectQueryData之间也需要间隔一定时间

    使用方法如下:

    1 int main()
    2 {
    3     init(0);
    4     while(1)
    5     {
    6         Sleep(800);
    7         printf("
    %f
    ", getCpuUsageRate());
    8     }
    9 }

    利用上述方法获取cpu当前利用率后,再画正弦曲线,只需要改变进程的busy时间和idle时间,如果当前点曲线需要的cpu利用率是a%,cpu实际利用率是b%

    若a>b, 那么进程的busy时间为该点时间片的(a-b)%

    若a<=b,那么进程busy时间为0(实际情况中由于cpu使用率采集的不精确以及使用率的不断变化,busy时间设置为0效果不一定最好,本文中是设置为原来时间的3/4)

    实际上除了当前进程外,如果cpu一直占用某个使用率,会影响曲线的形状,特别是曲线的下部分.

    代码如下:

      1 #include <TCHAR.h>
      2 #include <windows.h>
      3 #include <pdh.h>
      4 #include <cstdio>
      5 #include <cmath>
      6 #pragma comment(lib, "pdh.lib")
      7 
      8 //---------------------------------------------------comput the cpu usage rate
      9 static PDH_HQUERY cpuQuery;
     10 static PDH_HCOUNTER cpuTotal;
     11 
     12 //cpuindex 为指定的cpu id ,从0开始
     13 void init(int cpuIndex)
     14 {
     15     PDH_STATUS Status = PdhOpenQuery(NULL, NULL, &cpuQuery);
     16     if (Status != ERROR_SUCCESS) 
     17     {
     18         printf("
    PdhOpenQuery failed with status 0x%x.", Status);
     19         exit(-1);
     20     }
     21     char buf[50];
     22     sprintf(buf, "\Processor(%d)\%% Processor Time",cpuIndex);
     23     PdhAddCounter(cpuQuery, LPCSTR(buf), NULL, &cpuTotal);
     24     PdhCollectQueryData(cpuQuery);
     25 }
     26 
     27 
     28 double getCpuUsageRate()
     29 {
     30     PDH_FMT_COUNTERVALUE counterVal;
     31     PdhCollectQueryData(cpuQuery);
     32     PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
     33     return counterVal.doubleValue;
     34 }
     35 
     36 //--------------------------------------------------------------------cpu sin
     37 //把一条正弦曲线0~2pi 之间的弧度等分200份抽样,计算每个点的振幅
     38 //然后每隔300ms设置下一个抽样点,并让cpu工作对应振幅时间
     39 const int samplingCount = 200; //抽样点数目
     40 const double pi = 3.1415926;
     41 const int totalAmplitude = 300; //每个抽样点对应时间片
     42 const double delta = 2.0/samplingCount;  //抽样弧度的增量
     43 
     44 DWORD busySpan[samplingCount];//每个抽样点对应的busy时间
     45 int idleSpan[samplingCount];//每个抽样点对应的idle时间
     46 
     47 //一个线程调用MakeUsageSin,并把该线程绑定到一个cpu,那么该cpu呈现正弦曲线
     48 DWORD WINAPI MakeUsageSin(LPVOID lpParameter)
     49 {
     50     DWORD startTime = 0;
     51     for(int j = 0; ; j = (j + 1) % samplingCount)
     52     {
     53         startTime = GetTickCount();
     54         DWORD realBusy = busySpan[j];
     55 
     56         double currentCpuUsageRate = getCpuUsageRate();
     57         if(currentCpuUsageRate < busySpan[j]*1.0/totalAmplitude)
     58             realBusy = (busySpan[j]*1.0/totalAmplitude - currentCpuUsageRate)*totalAmplitude;
     59         else
     60             realBusy *= 0.75; 
     61 
     62         while ((GetTickCount() - startTime) < realBusy);
     63         Sleep(idleSpan[j]);
     64     }
     65 }
     66 
     67 //如果cpuindex < 0 则所有cpu都显示正弦曲线
     68 //否则只有第 cpuindex个cpu显示正弦曲线
     69 //cpuindex 从 0 开始计数
     70 void CpuSin(int cpuIndex)
     71 {
     72     //计算 busySpan 和 idleSpan
     73     double radian = 0;
     74     int amplitude = totalAmplitude / 2;
     75     for (int i = 0; i < samplingCount; i++)
     76     {
     77         busySpan[i] = (DWORD)(amplitude + sin(pi*radian)*amplitude);
     78         idleSpan[i] = totalAmplitude - busySpan[i];
     79         radian += delta;
     80     }
     81 
     82     //获取系统cup数量
     83     SYSTEM_INFO SysInfo;
     84     GetSystemInfo(&SysInfo);
     85     int num_processors = SysInfo.dwNumberOfProcessors;
     86     if(cpuIndex + 1 > num_processors)
     87     {
     88         printf("error: the index of cpu is out of boundary
    ");
     89         printf("cpu number: %d
    ", num_processors);
     90         printf("your index: %d
    ", cpuIndex);
     91         printf("** tip: the index of cpu start from 0 **
    ");
     92         return;
     93     }
     94 
     95     if(cpuIndex < 0)
     96     {
     97         HANDLE* threads = new HANDLE[num_processors];
     98         for (int i = 0;i < num_processors;i++)
     99         {
    100             DWORD mask = 1<<i;
    101             threads[i] = CreateThread(NULL, 0, MakeUsageSin, &mask, 0, NULL);
    102             SetThreadAffinityMask(threads[i], 1<<i);//线程指定在某个cpu运行
    103         }
    104         WaitForMultipleObjects(num_processors, threads, TRUE, INFINITE);
    105     }
    106     else
    107     {
    108         init(cpuIndex);
    109         HANDLE thread;
    110         DWORD mask = 1;
    111         thread = CreateThread(NULL, 0, MakeUsageSin, &mask, 0, NULL);
    112         SetThreadAffinityMask(thread, 1<<cpuIndex);
    113         WaitForSingleObject(thread,INFINITE);
    114     }
    115 
    116 }
    117 //-------------------------------------
    118 
    119 int main()
    120 {
    121     CpuSin(0);
    122 }

    主要改动在MakeUsageSin函数,初始化在上面代码108行

    结果如下:

    【版权声明】转载请注明出处 http://www.cnblogs.com/TenosDoIt/p/3242910.html

  • 相关阅读:
    基于SpringBoot实现请求的数据权限验证 规格严格
    PostgreSQL大版本升级(pg_upgrade)11.7升12.2 规格严格
    PostgreSQL还原数据库时出现问题:pg_restore: [archiver] unsupported version (1.13) in file header 规格严格
    mysql 如何去除表数据当中的回车符,换行符,空格和水平制表符?
    mysql 如何修改主键起始值?
    ASCII对照表
    [Unit testing RxJS] Test error handling with marbles
    [Typescript] 49. Medium PartialByKeys
    [Typescript] 46. Medium PickByType
    [Typescript] 48. Medium EndsWith
  • 原文地址:https://www.cnblogs.com/TenosDoIt/p/3242910.html
Copyright © 2020-2023  润新知