《编程之美》学习笔记——指挥CPU占用率

问题:

写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率(单核)。有以下几种情况:

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

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

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

4.多核处理器情况下上述问题怎么解决

分析与解答

首先确定CPU占用率的定义,即在任务管理器的一个刷新周期内,CPU忙(执行应用程序)的时间和刷新周期总时间的比率,就是CPU的占用率,也可以说成,任务管理器中显示的是每个刷新周期内CPU占用率的统计平均值。

所以可以写个程序,在一个刷新周期中,一会儿忙,一会儿闲,调节忙/闲比例,就可以控制CPU占有率了。

一个刷新时间是多久,书上说,通过对任务管理器观测,大约是1秒。鼠标移动、后台程序等都会对曲线造成影响!

单核环境下,空死循环会导致100%的CPU占用率。双核环境下,CPU总占用率大约为50%,四核是25%左右。

解法一:简单解法

Busy用可循环来实现,for(i=0;i<n;i++) ;

对应的汇编语言为

loop;
mov dx i     ;将i置入dx寄存器
inc dx       ;将dx寄存器加1
mov dx i     ;将dx中的值赋回i
cmp i n      ;比较i和n
j1 loop      ;i小于n时则重复循环

我的cpu是 I5 2410M 2.30GHZ(双核四线程,如图)  因为目前的cpu每个时钟周期可执行两条以上的代码,取平均值2,于是(2300000000*2)/5=920000000(循环/秒) 每秒可以执行循环920000000次。

不能简单的取n=920000000然后sleep(1000),如果让cpu工作1s,休息1s很可能是锯齿,先达到一个峰值然后跌入一个很低的占有率;所以我们睡眠时间改为100ms,100ms比较接近windows的调度时间,n=92000000。如果sleep时间选的太小,会造成线程频繁的唤醒和挂起,无形中增加了内核时间的不确定性因此代码如下:

#include <windows.h>  

int main(void)  
{  
<span style="white-space:pre">	</span>//Run on CPU 0(0x00000001)(00000001)
<span style="white-space:pre">	</span>//Run on CPU 1(0x00000002)(00000010)
<span style="white-space:pre">	</span>//Run on CPU 0 AND CPU 1(0x00000003)(00000101)
<span style="white-space:pre">	</span>//Run on CPU 2(0x00000004)(00000100)
<span style="white-space:pre">	</span>//......
<span style="white-space:pre">	</span>//SetProcessAffinityMask(GetCurrentProcess(),0x1);//进程与指定cpu绑定
<span style="white-space:pre">	</span>SetThreadAffinityMask(GetCurrentThread(), 0x1);//线程与指定cpu绑定
<span style="white-space:pre">	</span>while(true)  
<span style="white-space:pre">	</span>{  
<span style="white-space:pre">		</span>for(int i=0;i<92000000;i++)  
<span style="white-space:pre">			</span>;  
<span style="white-space:pre">		</span>Sleep(100);  
<span style="white-space:pre">	</span>}  
<span style="white-space:pre">	</span>return 0;  
}

使用SetProcessAffinityMask函数,进程与CPU绑定,得到如下图。

使用SetThreadAffinityMask函数,进程与CPU绑定,得到如下图。

解法二:使用GetTickCount()和Sleep()

GetTickCount()可以得到“系统启动到现在”所经历的时间的毫秒值,最多可以统计49.7天,可以利用GetTickCount()判断循环的时间,代码如下:

#include <windows.h>  

const int busyTime=100;
const int idleTime=busyTime;

int main(void)
{
	double startTime;
	SetProcessAffinityMask(GetCurrentProcess(), 0x1);
	while(true)
	{
		startTime=GetTickCount();
		while((GetTickCount() - startTime) <= busyTime)
		{
			;
		}
		Sleep(idleTime);
	}
	return 0;
} 

效果如下图所示,与第一种解法效果差不多,因为都假设当前系统只有当前程序在运行,但实际上,操作系统有很多程序会同时调试执行各种任务,如果此刻进程使用20%的cpu,那我们的程序只有使用30%的cpu才能达到50%的效果。

解法三:能动态适应的解法

使用.Net Framework提供的PerformanceCounter进行查询。具体代码是用C#写的

using System;
using System.Diagnostics;
namespace CPU
{
    class Program
    {
        static void Main(string[] args)
        {
           cpu(1000);
        }
        static void cpu(double level)
        {
            PerformanceCounter p = new PerformanceCounter("Processor", "% Processor Time", "_Total");
            if (p == null)
            {
                return;
            }
            while (true)
            {
                if (p.NextValue() > level)
                    System.Threading.Thread.Sleep(1000);
            }
        }
    }
}

解法四:正弦曲线

#include <windows.h>
#include <math.h>
int main(void)
{
	SetProcessAffinityMask(GetCurrentProcess(), 0x1);
	const double SPLIT=0.01;
	const int COUNT=200;
	const double PI=3.14159265;
	const int INTERVAL=300;
	DWORD busySpan[COUNT]; //array of busy time
	DWORD idleSpan[COUNT]; //array of idle time
	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;
}  

进一步讨论:

控制CPU占用率,因为要调用Windows的API,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很烦人。(taskmgr调用了一个未公开的API)。对CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。

如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。如果CPU占用率曲线不是周期性变化,就要对每个t值都要计算一次,否则,可以只计算第一个周期内的各个t值,其它周期的直接取缓存计算结果。

#include<iostream>
#include<cmath>
#include<windows.h>  

static int PERIOD = 60 * 1000; //周期ms
const int COUNT = 300;  //一个周期计算次数
const double GAP_LINEAR = 100;  //线性函数时间间隔100ms
const double PI = 3.1415926535898; //PI
const double GAP = (double)PERIOD / COUNT; //周期函数时间间隔
const double FACTOR = 2 * PI / PERIOD;  //周期函数的系数
static double Ratio = 0.5;  //线性函数的值 0.5即50%
static double Max=0.9; //方波函数的最大值
static double Min=0.1; //方波函数的最小值  

typedef double Func(double);  //定义一个函数类型 Func*为函数指针
typedef void Solve(Func *calc);//定义函数类型,参数为函数指针Func* 

inline DWORD get_time()
{
    return GetTickCount(); //操作系统启动到现在所经过的时间ms
}  

double calc_sin(double x)  //调用周期函数solve_period的参数
{
    return (1 + sin(FACTOR * x)) / 2; //y=1/2(1+sin(a*x))
}
double calc_fangbo(double x)  //调用周期函数solve_period的参数
{
    //方波函数
    if(x<=PERIOD/2) return Max;
    else return Min;
}  

void solve_period(Func *calc) //线程函数为周期函数
{
    double x = 0.0;
    double cache[COUNT];
    for (int i = 0; i < COUNT; ++i, x += GAP)
        cache[i] = calc(x);
    int count = 0;
    while(1)
    {
        unsigned ta = get_time();
        if (count >= COUNT) count = 0;
        double r = cache[count++];
        DWORD busy = r * GAP;
        while(get_time() - ta < busy) {}
        Sleep(GAP - busy);
  }
}  

void solve_linear(Func*)  //线程函数为线性函数,参数为空 NULL
{
    const unsigned BUSY =  Ratio * GAP_LINEAR;
    const unsigned IDLE = (1 - Ratio) * GAP_LINEAR;
    while(1)
    {
        unsigned ta = get_time();
        while(get_time() - ta < BUSY) {}
        Sleep(IDLE);
    }
}  

void run(int i=1,double R=0.5,double T=60000,double max=0.9,double min=0.1)
	//i为输出状态,R为直线函数的值,T为周期函数的周期,max方波最大值,min方波最小值
{
	Ratio=R; PERIOD=T; Max=max; Min=min;
	Func *func[] = {NULL ,calc_sin,calc_fangbo};  //传给Solve的参数,函数指针数组
	Solve *solve_func[] = { solve_linear, solve_period};  //Solve函数指针数组
	SYSTEM_INFO info;
	GetSystemInfo(&info);   //得到cpu数目
	int NUM_CPUS = info.dwNumberOfProcessors;
	HANDLE *handle = new HANDLE[NUM_CPUS];
	DWORD *thread_id = new DWORD[NUM_CPUS]; //线程id  

	switch(i)
	{
	case 1: //cpu0 ,cpu1都输出直线
		{
			for (int i = 0; i < NUM_CPUS; ++i)
			{
				Func *calc = func[0];
				Solve *solve = solve_func[0];
				if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
					(VOID*)calc, 0, &thread_id[i])) != NULL)  //创建新线程
					SetThreadAffinityMask(handle[i], i); //限定线程运行在哪个cpu上
			}
			WaitForSingleObject(handle[0],INFINITE);   //等待线程结束
			break;
		}
	case 2: //cpu0直线,cpu1正弦
		{
			if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],
				(VOID*)func[0], 0, &thread_id[1])) != NULL)  //创建新线程
				SetThreadAffinityMask(handle[1], 1); //限定线程运行在哪个cpu上
			Func *calc = func[1];
			Solve *solve = solve_func[1];
			if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
				(VOID*)calc, 0, &thread_id[0])) != NULL)  //创建新线程
				SetThreadAffinityMask(handle[0], 2); //限定线程运行在哪个cpu上
			WaitForSingleObject(handle[0],INFINITE);   //等待线程结束
			break;
		}
	case 3: //cpu0直线,cpu1方波
		{
			if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[0],
				(VOID*)func[0], 0, &thread_id[0])) != NULL)  //创建新线程
				SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
			Func *calc = func[2];
			Solve *solve = solve_func[1];
			if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
				(VOID*)calc, 0, &thread_id[1])) != NULL)  //创建新线程
				SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
			WaitForSingleObject(handle[0],INFINITE);   //等待线程结束
			break;
		}
	case 4: //cpu0正弦,cpu1方波
		{
			if ((handle[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve_func[1],
				(VOID*)func[1], 0, &thread_id[0])) != NULL)  //创建新线程
				SetThreadAffinityMask(handle[0], 1); //限定线程运行在哪个cpu上
			Func *calc = func[2];
			Solve *solve = solve_func[1];
			if ((handle[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
				(VOID*)calc, 0, &thread_id[1])) != NULL)  //创建新线程
				SetThreadAffinityMask(handle[1], 2); //限定线程运行在哪个cpu上
			WaitForSingleObject(handle[0],INFINITE);   //等待线程结束
			break;
		}
	default: break;
	}
}  

void main()
{
    //run(1,0.5);  //cpu1 ,cpu2都输出50%的直线
   run(2,0.5,30000); //cpu1 0.5直线,cpu2正弦周期30000
    //run(3);  //cpu1直线,cpu2方波
    //run(4,0.8,30000,0.95,0.5); //cpu1正弦,cpu2 0.95-0.5的方波
}  

这是是第一次CPU跑直线,第二个CPU跑正弦函数~~~~~~~~·

时间: 2024-11-07 00:41:13

《编程之美》学习笔记——指挥CPU占用率的相关文章

编程之美学习笔记(一)-让CPU占用率曲线听你指挥

终于放假了,开始在家里看一本向同学借来被程序猿尊称的必看经典书之一---<编程之美>.这本书给我的第一感觉是,他不单单是一本介绍算法的书,更不是一本纯算法的书,更多的是跟生活的很多实际问题息息相关,让你能更多地思考当我们面对现实实际问题的时候如何用自己从之前书本学习到的理论的知识去解决他们,如何以理论联系实际.这个寒假都会更新从这本书中学习来的知识,也希望有相同兴趣的人可以一起共同交流.那么就开始进入正题吧. 如何让cpu占用率呈现直线或者正弦曲线呢,看完第一章节,最简单的无非是调整自己写的程

编程之美学习笔记之 一摞烙饼的排序

编程之美书中讲的一摞烙饼的排序一题 这里无法用基本的排序方法对其排序,那么最直接的方法是找出N个数种最大者,将这通过两次翻转放置到最底部,然后处理N-1,N-2等,直到全部排序完,所以一共需要交换2(N-1)次 void reverse(int cakes[], int beg, int end) { int temp; while(beg < end){ temp = cakes[beg]; cakes[beg++] = cakes[end]; cakes[end--] = temp; } }

【编程之美学习笔记】2.1求二进制数中1的个数

问题:对于一个字节(8bit)的无符号整型变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能高. 解法一:除.余操作 我们知道,对于二进制操作,除以一个2,原来的数字将会减少一个0,如果除的过程中有余,那么就表示当前位置有一个1,所以可通过相除和判断余数的值来分析. [时间复杂度O(log2v),log2v为二进制数的位数,空间复杂度O(1)] 1 int Count(int v){ 2 int num = 0; 3 while(v){ 4 if(v % 2 == 1) num++;

【编程之美学习笔记】2.2不要被阶乘吓到

我好饿吖, 为什么我现在在教室闻到了饭香味... 问题1.给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N=3 628 800, N!的末尾有两个0. (好巧吖,昨天做的51nod 1003就是这个题,来分析一下吧!) 看到这题,你想完整计算N!的值吗?那可能溢出哦.其实这个问题只要从“哪些数相乘能得到10”这个角度考虑就简单啦. 首先,如果N!=k * 10^M, 且K不能被10整除,那么N!的末尾有M个0.再对N!进行质因数分解,N!=(2^X)*(3^Y)*(5^Z)…

编程之美读书笔记1.1——让CPU占用率曲线听你的指挥

http://blog.csdn.net/pipisorry/article/details/36189155 <strong><span style="font-size:48px;color:#ff0000;">问题:</span></strong>写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率. 假设机器是多CPU,上面的程序会出现什么结果?怎样在多个CPU时显示相同的状态?比如.在双核的

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

昨天在bbs上淘到了这本编程之美.顺手刷了第一章,很有意思.第一章的要求是要控制CPU曲线,绘制出对应的形状. 拿到这个问题,我的第一反应是, 是不是有这么一个API,能在任务管理器上的对应区域直接绘制图形呢? 然后就去查找API, 可惜搜索能力不行,最终还是没有找到. 然后看书上的解释, 太棒了. 解决这道题目的核心是. CPU占用率的概念,应该是指 CPU忙的时间与总时间的比,他是一个平均值的概念.也就是说.通过控制CPU忙闲时间的比值,我们能够大致控制CPU的占用率. 通过这个思想能够 控

cpu占用率曲线-笔记

我们知道要查看电脑的cpu占用率的话可以从任务管理器得知,进一步可以从->性能栏 看到CPU占用率的曲线的变化.今天看了编程之美的第一章第一节,看到对电脑cpu占用率的控制,控制CPU占用率曲线变化,达到特定的效果. cpu占用率曲线的刷新频率为1秒.要使在某一时刻CPU占用率达到目的中的百分比,可通过分配1秒内程序运行和休眠的时间的所占比,来达到.如在某1秒内,程序都处于休眠状态,那么,CPU占用率将达到0(理想化).反正,cpu占用率将达到100%. 第一种方法:基于计算程序所运行的电脑CP

编程之美读书笔记1.8 - 小飞的电梯调度算法

http://blog.csdn.net/pipisorry/article/details/36688019 问题: 亚洲微软研究院所在的希格玛大厦一共有6部电梯.在高峰时间,每层都有人上下,电梯每层都停.实习生小飞常常会被每层都停的电梯弄的很不耐烦,于是他提出了这样一个办法: 由于楼层并不算太高,那么在繁忙的上下班时间,每次电梯从一层往上走时,我们只允许电梯停在其中的某一层.所有乘客从一楼上电梯,到达某层后,电梯停下来,所有乘客再从这里爬楼梯到自己的目的层.在一楼的时候,每个乘客选择自己的目

Shell编程检测监控mysql的CPU占用率

shell编程很强大! 网站访问量大的时候mysql的压力就比较大,当mysql的CPU利用率超过300%的时候就不能提供服务了,近乎卡死状态,这时候最好的方法就是重启mysql服务.由于这种事具有不可预见性,我们不知道什么时候mysql的占用率达到300%,还是写个程序定期判断比较靠谱. 学了shell编程,写了下面的脚本: #!/bin/bash cpu=`ps aux | grep 'mysqld$' | cut -d " " -f6 | cut -d. -f1` if [ $c