第1章 游戏之乐——让CPU占用率曲线听你指挥
让CPU占用率曲线听你指挥
写一个程序,让用于来决定Windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:
- CPU的占用率固定在50%,为一条直线;
- CPU的占用率为一条直线,但是具体占用率由命令行参数决定(参数范围1~100);
- CPU的占用率状态是一个正弦曲线。
【解法一】 简单的解法
一个空的for循环for(int i=0;i<n;i++); 写成汇编代码后进行分析:
loop:
mov dx i ;将i置入dx寄存器
inc dx ;将dx从寄存器加1
mov i dx ;将dx中的值赋回i
cmp i n ;比较i和n
jl loop ; i小于n时则重复循环
需要执行5条代码,假设要运行的CPU是2.66Ghz(2.66*10^9个时钟周期每秒)。现在CPU每个时钟周期可以执行两条以上的代码,那么我们就取平均值两条,于是让(2660000000*2)/5=1064000000(循环/秒),如果让CPU工作1秒钟,然后休息1秒钟,波形很可能就是锯齿状的——先达到一个峰值(>50%),然后跌到一个很低的占用率。
于是尝试降两个数量级。用10ms是因为它不大不小,比较接近Windows的调度时间片。如果太小会造成线程频繁被唤醒和挂起,无形中增加了内核时间的不确定性影响。最后我们得到如下代码:
package chapter1youxizhile; /** * 【解法一】简单的方法 * 运行的CPU是2.66Ghz(2.66*10^9个时钟周期每秒) * @author DELL * */ public class ControlCPU1 { public static void main(String[] args) { for( ; ; ){ for(int i=0;i<10640000;i++); //运行10ms try { Thread.sleep(10); //休眠10ms } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
在不断调整10640000后,我们就可以在一台指定的机器上获得一条大致稳定的50%CPU占用率直线。
【解法二】使用System.currentTimeMillis()和Thread.sleep()
package chapter1youxizhile; /** * 【解法二】使用System.currentTimeMillis()和Thread.sleep() * 运行的CPU是2.66Ghz(2.66*10^9个时钟周期每秒) * @author DELL * */ public class CopyOfControlCPU2 { public static void main(String[] args) { int busyTime = 10; //繁忙时间10ms int idleTime = busyTime; //空闲时间 while(true){ long startTime = System.currentTimeMillis(); //获取系统运行时间 while(System.currentTimeMillis()-startTime<= busyTime) ; try { Thread.sleep(idleTime); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
效果如下:
这两种解法都是假设目前系统上只有当前程序在运行,但实际上,操作系统中有很多程序会同时执行各种各样的任务,如果此刻其他进程使用了10%的CPU,那么我们的程序应该只能使用40%的CPU,这样才能达到50%的效果。
怎么做呢?这就需要用到另个工具的帮忙——Perform.exe。如下图所示:
我们可以写程序来查询Perform的值,Mircrosoft .Net Framework提供了PerformanceCounter这一对象,可以方便地得到当前各种性能的数据,包括CPU的使用率。例如下面这个程序:
【解法三】能动态适应的解法
//C# code 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使用率的参数。这个程序可以解答前面提到的问题2。
【解法四】正弦曲线
package chapter1youxizhile; /** * 【解法四】正弦曲线 * @author DELL * */ public class CopyOfCopyOfControlCPU4 { static final double SPLIT = 0.01; //2PI周期步长 static final int COUNT = 200; //取点个数 static final double PI = 3.1415926; static final int INTERVAL = 300; //空闲和繁忙的总时间 public static void main(String[] args) { long busySpan[]; //CPU繁忙时间的数组 long idleSpan[]; //CPU空闲时间的数组 busySpan = new long[COUNT]; idleSpan = new long[COUNT]; int half = INTERVAL/2; double radian = 0.0; for(int i=0;i<COUNT;i++){ busySpan[i] = (long) (half+(Math.sin(PI*radian)*half)); idleSpan[i] = INTERVAL - busySpan[i]; radian += SPLIT; } long startTime = 0; int j = 0; while(true){ j = j%COUNT; //实现周期循环 startTime = System.currentTimeMillis(); while((System.currentTimeMillis()-startTime)<=busySpan[j]) ; try { Thread.sleep(idleSpan[j]); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } j++; } } }
程序运行后任务管理器效果如下: