你带酒来,我有故事

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

:: 代码生涯 二十画生 972℃ 0评论

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

写一个程序,让用户来决定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;
}

运行结果:

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

转载请注明:二十画生 » 编程之美 — 让CPU占用率绘制任意图形

喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址