本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

编程之美 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\n");
49         printf("cpu number: %d\n", num_processors);
50         printf("your index: %d\n", cpuIndex);
51         printf("** tip: the index of cpu start from 0 **\n");
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\n", GetLastError());
 7         getch();
 8         return -1;
 9     }
10     else
11     {
12         while (true)
13         {    
14             Sleep(1000);
15             printf("\r当前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("\nPdhOpenQuery 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("\n%f\n", 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("\nPdhOpenQuery 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\n");
 89         printf("cpu number: %d\n", num_processors);
 90         printf("your index: %d\n", cpuIndex);
 91         printf("** tip: the index of cpu start from 0 **\n");
 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

posted @ 2013-08-07 13:14  tenos  阅读(7572)  评论(10编辑  收藏  举报

本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

公益页面-寻找遗失儿童