编程之美读书笔记_1.1_让CPU占用率曲线听你指挥

题目:写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:

 

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

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

3.    CPU的占用率状态是一个正弦曲线。

 

控制CPU占用率,不仅仅是出于好玩而已。以前的某些程序,特别是某些老游戏,在新的机器上运行速度太快,必须先给CPU降速,才能顺利运行那些程序,有个共享软件CPUKiller,就是专门弄这个的。

 

控制CPU占用率,因为要调用WindowsAPI,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很烦人。(taskmgr调用了一个未公开的API)

 

CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfoSYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。不知道超线程对这两种方法有什么影响。

 

如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。

 

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

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

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

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

因此,很容易写出下面这个通用函数:

 

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值为60s60000ms,则

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

 

 

完整代码如下:

 

#include<windows.h>

#include<cstdio>

#include<cmath>

 

const int PERIOD = 60 * 1000; //60,000 ms

const int COUNT = 200;

 

const double PI = 3.1415926535898;

const double GAP = (double)PERIOD / COUNT;

const double FACTOR = 2 * PI / PERIOD; 

 

typedef double Func(double);

inline DWORD get_time() { return GetTickCount(); }

 

double calc2(double x) {  return (1 + sin(FACTOR * x)) / 2;}

 

double calc3(double)

{

  static double cache[COUNT];

  static int count = 0;

  static bool first = true;

  if (first) {

    double x = 0.0;

    for (int i = 0; i < COUNT; ++i, x += GAP)

      cache[i] = (1.0 + sin(FACTOR * x)) / 2.0;

    first = false;

  }

  if (count >= COUNT) count = 0;

  return cache[count++]; 

}

 

double calc4(double) { return 0.8;}

 

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;

  }

}

 

 

void run()

{

  Func *func[] = { calc2, calc3, calc4 };

  Func *calc = func[1];

  const int MAX_CPUS = 32;

  HANDLE handle[MAX_CPUS];

  DWORD thread_id[MAX_CPUS];

  SYSTEM_INFO info;

  GetSystemInfo(&info);

  const int num = info.dwNumberOfProcessors;

  for (int i = 0; i < num; ++i) {

    if ( (handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,

         (VOID*)calc, 0, &thread_id[i])) != NULL)

      SetThreadAffinityMask(handle[i], i + 1);

  }

  WaitForSingleObject(handle[0],INFINITE);  

}

 

 

int main()

{

  run();

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值