《编程之美》学习笔记——指挥CPU占用率

来源:互联网 发布:东航巴黎昆明 知乎. 编辑:程序博客网 时间:2024/05/02 05:03

问题:

写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率(单核)。有以下几种情况:
1.CPU占用率固定在50%,为一条直线
2.CPU的占用率为一条直线,具体占用率由命令行参数决定(范围1~100)
3.CPU的占用率状态为一条正弦曲线
4.多核处理器情况下上述问题怎么解决


分析与解答

首先确定CPU占用率的定义,即在任务管理器的一个刷新周期内,CPU忙(执行应用程序)的时间和刷新周期总时间的比率,就是CPU的占用率,也可以说成,任务管理器中显示的是每个刷新周期内CPU占用率的统计平均值。
所以可以写个程序,在一个刷新周期中,一会儿忙,一会儿闲,调节忙/闲比例,就可以控制CPU占有率了。
一个刷新时间是多久,书上说,通过对任务管理器观测,大约是1秒。鼠标移动、后台程序等都会对曲线造成影响!
单核环境下,空死循环会导致100%的CPU占用率。双核环境下,CPU总占用率大约为50%,四核是25%左右。


解法一:简单解法 

Busy用可循环来实现,for(i=0;i<n;i++) ;
对应的汇编语言为
loop;mov dx i     ;将i置入dx寄存器inc dx       ;将dx寄存器加1mov dx i     ;将dx中的值赋回icmp i n      ;比较i和nj1 loop      ;i小于n时则重复循环

我的cpu是 I5 2410M 2.30GHZ(双核四线程,如图)  因为目前的cpu每个时钟周期可执行两条以上的代码,取平均值2,于是(2300000000*2)/5=920000000(循环/秒) 每秒可以执行循环920000000次。

不能简单的取n=920000000然后sleep(1000),如果让cpu工作1s,休息1s很可能是锯齿,先达到一个峰值然后跌入一个很低的占有率;所以我们睡眠时间改为100ms,100ms比较接近windows的调度时间,n=92000000。如果sleep时间选的太小,会造成线程频繁的唤醒和挂起,无形中增加了内核时间的不确定性因此代码如下:

#include <windows.h>  int main(void)  {  //Run on CPU 0(0x00000001)(00000001)//Run on CPU 1(0x00000002)(00000010)//Run on CPU 0 AND CPU 1(0x00000003)(00000101)//Run on CPU 2(0x00000004)(00000100)//...... //SetProcessAffinityMask(GetCurrentProcess(),0x1);//进程与指定cpu绑定 SetThreadAffinityMask(GetCurrentThread(), 0x1);//线程与指定cpu绑定 while(true)  {   for(int i=0;i<92000000;i++)   ;   Sleep(100);   }   return 0;  } 

使用SetProcessAffinityMask函数,进程与CPU绑定,得到如下图。

使用SetThreadAffinityMask函数,进程与CPU绑定,得到如下图。


解法二:使用GetTickCount()和Sleep()

GetTickCount()可以得到“系统启动到现在”所经历的时间的毫秒值,最多可以统计49.7天,可以利用GetTickCount()判断循环的时间,代码如下:

#include <windows.h>  const int busyTime=100;  const int idleTime=busyTime;int main(void)  {   double startTime;  SetProcessAffinityMask(GetCurrentProcess(), 0x1);  while(true)  {  startTime=GetTickCount();  while((GetTickCount() - startTime) <= busyTime)  {  ;  }  Sleep(idleTime);  }  return 0;  } 
效果如下图所示,与第一种解法效果差不多,因为都假设当前系统只有当前程序在运行,但实际上,操作系统有很多程序会同时调试执行各种任务,如果此刻进程使用20%的cpu,那我们的程序只有使用30%的cpu才能达到50%的效果。

解法三:能动态适应的解法

使用.Net Framework提供的PerformanceCounter进行查询。具体代码是用C#写的
using System;using System.Diagnostics; namespace CPU{    class Program    {        static void Main(string[] args)        {           cpu(1000);          }          static void cpu(double level)          {              PerformanceCounter p = new PerformanceCounter("Processor", "% Processor Time", "_Total");              if (p == null)              {                  return;              }              while (true)              {                  if (p.NextValue() > level)                      System.Threading.Thread.Sleep(1000);              }          }      }}

解法四:正弦曲线

#include <windows.h>  #include <math.h>  int main(void)  {  SetProcessAffinityMask(GetCurrentProcess(), 0x1);  const double SPLIT=0.01;  const int COUNT=200;  const double PI=3.14159265;  const int INTERVAL=300;  DWORD busySpan[COUNT]; //array of busy time  DWORD idleSpan[COUNT]; //array of idle time  int half=INTERVAL/2;  double radian=0.0;  for(int i=0;i<COUNT;i++)  {  busySpan[i]=(DWORD)(half+(sin(PI*radian)*half));  idleSpan[i]=INTERVAL-busySpan[i];  radian+=SPLIT;  }  DWORD startTime=0;  int j=0;  while(true)  {  j=j%COUNT;  startTime=GetTickCount();  while((GetTickCount()-startTime)<=busySpan[j])  ;  Sleep(idleSpan[j]);  j++;  }  return 0;  }  

进一步讨论:

控制CPU占用率,因为要调用Windows的API,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很烦人。(taskmgr调用了一个未公开的API)。对CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。

如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。如果CPU占用率曲线不是周期性变化,就要对每个t值都要计算一次,否则,可以只计算第一个周期内的各个t值,其它周期的直接取缓存计算结果。

#include<iostream>  #include<cmath>  #include<windows.h>    static int PERIOD = 60 * 1000; //周期ms  const int COUNT = 300;  //一个周期计算次数  const double GAP_LINEAR = 100;  //线性函数时间间隔100ms  const double PI = 3.1415926535898; //PI  const double GAP = (double)PERIOD / COUNT; //周期函数时间间隔  const double FACTOR = 2 * PI / PERIOD;  //周期函数的系数  static double Ratio = 0.5;  //线性函数的值 0.5即50%  static double Max=0.9; //方波函数的最大值  static double Min=0.1; //方波函数的最小值    typedef double Func(double);  //定义一个函数类型 Func*为函数指针  typedef void Solve(Func *calc);//定义函数类型,参数为函数指针Func* inline DWORD get_time()   {       return GetTickCount(); //操作系统启动到现在所经过的时间ms  }  double calc_sin(double x)  //调用周期函数solve_period的参数  {        return (1 + sin(FACTOR * x)) / 2; //y=1/2(1+sin(a*x))  }  double calc_fangbo(double x)  //调用周期函数solve_period的参数  {      //方波函数      if(x<=PERIOD/2) return Max;      else return Min;  }    void solve_period(Func *calc) //线程函数为周期函数  {      double x = 0.0;      double cache[COUNT];      for (int i = 0; i < COUNT; ++i, x += GAP)           cache[i] = calc(x);       int count = 0;      while(1)      {          unsigned ta = get_time();          if (count >= COUNT) count = 0;          double r = cache[count++];          DWORD busy = r * GAP;          while(get_time() - ta < busy) {}          Sleep(GAP - busy);    }  }    void solve_linear(Func*)  //线程函数为线性函数,参数为空 NULL  {      const unsigned BUSY =  Ratio * GAP_LINEAR;      const unsigned IDLE = (1 - Ratio) * GAP_LINEAR;      while(1)      {          unsigned ta = get_time();          while(get_time() - ta < BUSY) {}          Sleep(IDLE);      }  }    void run(int i=1,double R=0.5,double T=60000,double max=0.9,double min=0.1)  //i为输出状态,R为直线函数的值,T为周期函数的周期,max方波最大值,min方波最小值  {  Ratio=R; PERIOD=T; Max=max; Min=min; Func *func[] = {NULL ,calc_sin,calc_fangbo};  //传给Solve的参数,函数指针数组  Solve *solve_func[] = { solve_linear, solve_period};  //Solve函数指针数组  SYSTEM_INFO info;  GetSystemInfo(&info);   //得到cpu数目  int NUM_CPUS = info.dwNumberOfProcessors;HANDLE *handle = new HANDLE[NUM_CPUS];    DWORD *thread_id = new DWORD[NUM_CPUS]; //线程id  switch(i)  {  case 1: //cpu0 ,cpu1都输出直线  {  for (int i = 0; i < NUM_CPUS; ++i)  {  Func *calc = func[0];  Solve *solve = solve_func[0];  if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,   (VOID*)calc, 0, &thread_id[i])) != NULL)  //创建新线程  SetThreadAffinityMask(handle[i], i); //限定线程运行在哪个cpu上  }  WaitForSingleObject(handle[0],INFINITE);   //等待线程结束  break;  }  case 2: //cpu0直线,cpu1正弦  {  if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],   (VOID*)func[0], 0, &thread_id[1])) != NULL)  //创建新线程  SetThreadAffinityMask(handle[1], 1); //限定线程运行在哪个cpu上   Func *calc = func[1];  Solve *solve = solve_func[1];  if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,   (VOID*)calc, 0, &thread_id[0])) != NULL)  //创建新线程  SetThreadAffinityMask(handle[0], 2); //限定线程运行在哪个cpu上 WaitForSingleObject(handle[0],INFINITE);   //等待线程结束  break;  }  case 3: //cpu0直线,cpu1方波  {  if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],   (VOID*)func[0], 0, &thread_id[0])) != NULL)  //创建新线程  SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上  Func *calc = func[2];  Solve *solve = solve_func[1];  if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,   (VOID*)calc, 0, &thread_id[1])) != NULL)  //创建新线程  SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上  WaitForSingleObject(handle[0],INFINITE);   //等待线程结束  break;  }  case 4: //cpu0正弦,cpu1方波  {  if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[1],   (VOID*)func[1], 0, &thread_id[0])) != NULL)  //创建新线程  SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上  Func *calc = func[2];  Solve *solve = solve_func[1];  if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,   (VOID*)calc, 0, &thread_id[1])) != NULL)  //创建新线程  SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上  WaitForSingleObject(handle[0],INFINITE);   //等待线程结束  break;  }  default: break;  }  }    void main()  {      //run(1,0.5);  //cpu1 ,cpu2都输出50%的直线     run(2,0.5,30000); //cpu1 0.5直线,cpu2正弦周期30000      //run(3);  //cpu1直线,cpu2方波      //run(4,0.8,30000,0.95,0.5); //cpu1正弦,cpu2 0.95-0.5的方波  }  

这是是第一次CPU跑直线,第二个CPU跑正弦函数~~~~~~~~·




1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 高一英语成绩差怎么办 文章被同时录用该怎么办 来不及开ei检索证明怎么办 淘宝生产许可编号一定要填怎么办 官网下单被砍单怎么办美卡美私 大学网络课程挂了怎么办 如果二审超过6个月还不判怎么办? sci的proof时间超了怎么办 合肥电大考试没过怎么办 社保账号密码忘记了怎么办 住房公积金账号密码忘记了怎么办 燃气费单子丢了怎么办 商标初审公告期内被异议怎么办 手被山药痒了怎么办 9个月宝宝不吃奶粉怎么办 八个月宝宝拉粑粑费劲怎么办? 两个月小孩不吃奶粉怎么办 两个月的小孩不吃奶粉怎么办 两个多月宝宝不吃奶怎么办 三个多月宝宝不爱吃奶怎么办 4个月宝宝不吃奶怎么办 5个月宝宝不爱吃奶怎么办 九个月宝宝一直流鼻涕怎么办 九个月宝宝一直咳嗽怎么办 宝宝3岁不爱喝水怎么办 1岁宝宝不肯喝水怎么办 三个月宝宝体检说严重缺钙怎么办 1岁半宝宝不吃药怎么办 1岁宝宝抗拒吃药怎么办 六个月宝宝不爱吃辅食怎么办 宝宝九个月了不爱吃辅食怎么办 八个月宝宝不喜欢吃辅食怎么办 小孩米粉吃多了怎么办 宝宝四个月了奶水不足怎么办 4个月奶水不足怎么办 孩子不吃奶粉母乳又不够怎么办 宝宝吃母乳上火了怎么办 5个月宝宝厌奶期怎么办 九个月宝宝不吃奶粉怎么办 第5个月奶不够吃怎么办 九个月的宝宝不吃奶粉怎么办