淡水求咸

博客园已经停止更新,请移步 http://lovecjh.com/

导航

编程之美 — 让CPU占用率绘制任意图形

  《编程之美》第一章给出了一个面试题:

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

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

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

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

  最一般的思路:CPU占用率为一条直线,首先要搞清楚什么是CPU占用率,CPU占用率是你运行的程序占用的CPU资源,表示你的机器在某个时间点的运行程序的情况。其实在某一时间点,CPU要么被占用,要么没被占用,即占用率要么为1,要么为0;那为什么还有占用率为50%之说呢,其实这里的时间指的一段时间,这个时间对人来可能就是滴答一下就流逝了,人的感觉可能是一个时间点,但对于机器来说,这就是一个时间段,时间周期。这样就好理解了,在一段时间内,CPU占用率就是CPU被占用的时间除以这段时间的总时间,即:

  CPU占用率 = CPU被占用的时间 / 总时间

  在《编程之美》中已经给出了根据CPU主频计算for(int i = 0; i < n; i++)循环n的大小,我的电脑的主频是2.00GHZ,所以for里面运行1s才跳出的n的大小为:

  2 × 109 × 2 ÷ 5 = 8 × 108,其中后面乘以2表示CPU每个时钟周期执行两条代码,而for(int i = 0; i < n; i++);转成汇编是5条,所以除以5。

  为了接近Windows的调试时间片,取Sleep(10),那么此时,n取8000000。

  具体程序如下:

#include <iostream>
#include <windows.h>
using namespace std;

int main()
{
    while(true)
    {
        for(int i = 0; i < 8000000; i++)
        {
            ;
        }

        Sleep(10);
    }

    return 0;
}

  运行结果:

  结果几近一条直线,但有较大的波动,特别是你还在运行其它程序时,比如拖到鼠标,波动会更大。

  上面计算存在许多近似:n计算的近似;以for()循环汇编代码的条数代替CPU时钟周期执行的代码数等等。

  可利用GetTickCount()更精确获取系统时间,GetTickCount()获取的是系统到“现在”所经历时间的毫秒数。

#include <iostream>
#include <windows.h>
using namespace std;
int main() { _int64 start_time = 0; int run_time = 10; int sleep_time = run_time; while(true) { start_time = GetTickCount(); while((GetTickCount() - start_time) <= run_time); Sleep(sleep_time); } return 0; }

  运行结果:

  从结果可以看出,直线的波动较小,GetTickCount()可获取更精确的系统时间。

  下面来让CPU占用率绘制正弦曲线:

  下面是《编程之美》上的代码:

#include <Windows.h>
#include <stdlib.h>
#include <math.h>

const double SPLIT = 0.01;
const int COUNT = 200;
const double PI = 3.13159265;
const int INTERVAL = 300;

int main()
{
    DWORD busySpan[COUNT];
    DWORD idleSpan[COUNT];
    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;
}

  运行结果:

  这里主要讲下下面代码的意思:

 for(int i = 0; i < COUNT; i++)
    {
        busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));
        idleSpan[i] = INTERVAL - busySpan[i];
        radian += SPLIT;

    }

  我们知道绘制一条正弦曲线,只要知道0~2∏之间的CPU占用率的值就行了,其它就是周期移动就行了。

  题目要求CPU占用率为正弦曲线,所以不仿设busySpan[i] / (busySpan[i] + idleSpan[i]) = asin(∏ti + φ) + b;

  还知道busySpan[i] / (busySpan[i] + idleSpan[i]) + idleSpan[i] / (busySpan[i] + idleSpan[i]) = 1,这里(busySpan[i] + idleSpan[i])等于INTERVAL。综合上面两个方面,可取φ = 0;a = b = INTERVAL/2。

  ∏ × SPLIT × COUNT = 2∏,正好为正弦函数的一个周期。

  扩展:如何让CPU占用率为半圆形曲线

  参考程序如下:

#include <windows.h>
#include <stdlib.h>
#include <math.h>

const double SPLIT = 0.01;
const int COUNT = 200;
const int INTERVAL = 300;

int main()
{
    DWORD busySpan[COUNT];
    DWORD idleSpan[COUNT];
    double radian = 0.0;
    for(int i = 0; i < COUNT; i++)
    {
        busySpan[i] = (DWORD)(INTERVAL * sqrt(1 - pow((1 - radian) , 2)));
        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;
}

  运行结果:

  当然你还可以让它绘制出更漂亮的图形。

posted on 2012-09-14 16:03  深圳彦祖  阅读(2802)  评论(0编辑  收藏  举报