《编程之美》-- 让CPU占用率听你指挥

题目要求

写一个程序,让用户决定windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算语言不限。例如,可以实现下面三种情况
1. CPU的占用率固定在50%,为一条直线;
2. CPU的占用率为一条直线,具体占用率由命令行参数决定(参数范围1~100);
3. CPU的占用率状态是一条正弦曲线

什么是CPU使用率

CPU执行应用的程序的时间和刷新周期总时间的比率,就是CPU使用率

操作CPU

要想CPU的使用率控制在50%(或者其他任意比例),我们首先要明白怎么让系统忙起来和闲起来。当一个程序运行的时候,系统就忙起来了。那怎么让它闲下来?很简单,当这些程序在等待用户输入,或者其他操作的时候,或者进入“休眠”的时候。

要操纵CPU的使用率曲线,就需要使CPU在一段时间内(根据任务管理器(Task Manager)的采样率)跑busy和idle两个不同的循环,从而通过不同的时间比率,来调节CPU的使用率

简单解法一

两个基本循环

busy : 我们使用一个for循环

for(int i = 0; i < n; ++i)

idle:我们使用Sleep函数

例如: 我们让程序停止10ms
Sleep(10)

这里重要的是知道n的值和需要程序休眠多少时间

什么是CPU频率?

CPU频率,就是CPU的时钟频率,简单说是CPU运算时的工作频率(1秒内发生的同步脉冲数)的简称

代码运行的CPU是P4 2.4GHZ 即 2.4 * 10^9
由于现代CPU每个时钟周期可以执行两条以上的代码,于是有

n =(2400000000 * 2)/ 5 = 960000000(循环秒)

Sleep(1000)

也就是说CPU1秒钟可以运行这个空循环960000000次

当然由于实际原因,会进行等比例缩放

所以取

n = 9600000

Sleep(10)

具体代码

因为本人的不是单核CPU,所以加了

SetThreadAffinityMask(GetCurrentThread(), 0x00000001);

目的是使这段代码运行在一个CPU里

#include 
#include 
#include 
using namespace std;

int main()
{
    SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
    for(;;)     // while(1)
    {
        for(int i = 0; i < 9600000; i++)
            ;
        Sleep(10);          // 本人电脑上这里要设置成Sleep(20)才能大致趋于50%
    }
    return 0;
}

《编程之美》-- 让CPU占用率听你指挥_第1张图片

上图具体如下

《编程之美》-- 让CPU占用率听你指挥_第2张图片

使用GetTickCount()和Sleep()

#include 
#include 
#include 

int main()
{
    SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
    int busyTime = 50;
    int idleTime = busyTime;

    for(;;)         // while(1)
    {
        int startTime = GetTickCount();
        while((GetTickCount() - startTime) < busyTime)
            ;
        Sleep(idleTime);
    }

    return 0;
}

在上面两种代码中都是假设目前没有其他程序运行,但实际上,我们往往会有很多进程,他们占用了或多或少的CPU,所以上面图中没有出现一条直线

能动态适应的解法

目前还不会C++的写法(可能会有某个API来获取当前的CPU使用率?performanceCounter?不过好像很麻烦),书上给出了C#的自适应解法,这里照搬下来

static void MakeUsage(float level)
{
    PerformanceCounter p = new PerformanceCounter("Processor", 
        "%Processor Time", "__Total");

    while(true) {
        if(p.NextValue() > level) {
            System.Threading.Thread.Sleep(10);
        }
    }
}

正弦曲线

分析

在系统中,CPU不可能真的画出正弦函数,而是有一个个时间点构成的连线,因此我在在正弦函数的一个周期中选择了200个坐标点,用于构成正弦函数图。

在正弦函数的选择上,我使用了最简单的

y=sin(x)

根据数据公式可以简单的知道:
1.周期

T = 2 * PI

2.点与点之间的时间间隔

t = T / 200 // T为周期,200为点的总数

3.busy和idle的时间

busy : busyTime[i] = sin(t * i)
idle:1 - busyTime[i]
// i 表示第几个点

因此得出了下面的代码

#include 
#include 
#include 

const int SAMPLING_COUNT = 200;     // 抽样点数量 
const double PI = 3.1415926535;         // pi值
const int TOTAL_AMPLITUDE = 300;    // 每个抽样点对应的时间片

int main()
{
    SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
    double busyTime[SAMPLING_COUNT];
    double radian = 0.0;
    double radianIncrement = 2 * PI / SAMPLING_COUNT;
    for(int i = 0; i < SAMPLING_COUNT; i++) {
        busyTime[i] =  sin(radianIncrement * i);
        radian += radianIncrement;
    }

    for(int j = 0; ; j = (j + 1) % SAMPLING_COUNT) {
        int startTime = GetTickCount();
        while((int)(GetTickCount() - startTime) < busyTime[j])
            ;
        Sleep(1 - busyTime[j]); 
    }

    return 0;
}

《编程之美》-- 让CPU占用率听你指挥_第3张图片

图的效果不是很好,因此把代码改为如下

#include 
#include 
#include 

const int SAMPLING_COUNT = 200;     // 抽样点数量 
const double PI = 3.1415926535;     // pi值
const int TOTAL_AMPLITUDE = 300;    // 每个抽样点对应的时间片

int main()
{
    SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
    double busyTime[SAMPLING_COUNT];
    double radian = 0.0;
    double radianIncrement = 2 * PI / SAMPLING_COUNT;
    int amplitude = TOTAL_AMPLITUDE / 2;
    for(int i = 0; i < SAMPLING_COUNT; i++) {
        busyTime[i] = amplitude + sin(radianIncrement * i) * amplitude;
        radian += radianIncrement;
    }

    for(int j = 0; ; j = (j + 1) % SAMPLING_COUNT) {
        int startTime = GetTickCount();
        while((int)(GetTickCount() - startTime) < busyTime[j])
            ;
        Sleep(TOTAL_AMPLITUDE - busyTime[j]);   
    }

    return 0;
}

《编程之美》-- 让CPU占用率听你指挥_第4张图片

看起来好多了

至此,上面的题已经完成了

拓展

画个三角形图案

代码如下:

#include 
#include 
#include 

int main()
{
    SetThreadAffinityMask(GetCurrentThread(), 0x00000001);
    int SAMPLING_COUNT = 200;
    double TOTAL_AMPLITUDE = 200;
    double busyTime[SAMPLING_COUNT];
    for(int i = 0; i < SAMPLING_COUNT; i++) {
        busyTime[i] = SAMPLING_COUNT / TOTAL_AMPLITUDE * i;
    }

    for(int j = 0; ; j = (j + 1) % SAMPLING_COUNT) {
        int startTime = GetTickCount();
        while((int)(GetTickCount() - startTime) < busyTime[j])
            ;
        Sleep(TOTAL_AMPLITUDE - busyTime[j]);   
    }

    return 0;
}

《编程之美》-- 让CPU占用率听你指挥_第5张图片

你可能感兴趣的:(编程之美)