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

来源:互联网 发布:网络推广课程 编辑:程序博客网 时间:2024/05/01 01:26

题目要求

写一个程序,让用户决定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 <iostream>#include <unistd.h>#include <windows.h>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 <windows.h>#include <stdio.h>#include <stdlib.h>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 <windows.h>#include <iostream>#include <math.h>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;}

正弦曲线1

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

#include <windows.h>#include <iostream>#include <math.h>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;}

正弦函数2

看起来好多了

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

拓展

画个三角形图案

代码如下:

#include <windows.h>#include <iostream>#include <math.h>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;}

三角形

0 0
原创粉丝点击