Creative Commons License
本Blog采用 知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议 进行许可。 —— Fox <游戏人生>

游戏人生

游戏人生 != ( 人生 == 游戏 )
站点迁移至:http://www.yulefox.com。请订阅本博的朋友将RSS修改为http://feeds.feedburner.com/yulefox
posts - 62, comments - 508, trackbacks - 0, articles - 7

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

Posted on 2008-04-17 00:20 Fox 阅读(11090) 评论(10)  编辑 收藏 引用 所属分类: T技术碎语

Author: Fox

前两天在买《计算机程序设计艺术》中文版的时候,偶然发现《编程之美》这本书,当时翻了一下,看到“让CPU占用率曲线听你指挥”这样的题目确实让人为之一动。写一段代码,可以让CPU占有率曲线画出平滑的正弦曲线,有点意思:-)。

当然,最后没有买这本书,虽然我可以肯定这是本好书。

我从CSDN读书上找到几节,闲来读一读。今天来讨论一下《让CPU占用率曲线听你指挥》。

题目:写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:

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

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

3.    CPU的占用率状态是一个正弦曲线。

在讨论具体实现之前,一个非常重要的问题是:什么是CPU占用率

编程之美》写道:“在任务管理器的一个刷新周期内,CPU忙(执行应用程序)的时间和刷新周期总时间的比率,就是CPU的占用率,也就是说,任务管理器中显示的是每个刷新周期内CPU占用率的统计平均值。

打开“Windows 任务管理器”,“性能”中有“CPU使用记录”一项,给出的就是CPU占有率曲线。

至于一个刷新周期到底是多长,书中似乎没有明确给出,只是说“大约是1秒钟更新一次”,我打开Windows自带的时钟,也感觉大约是1秒钟。

另外的常识是:

单核环境下,空死循环会导致100%的CPU占有率。双核环境下,CPU总占有率大约为50%,四核会不会是25%左右呢?(我没有四核,只能猜测了,估计各核间切换也会耗掉点时间,因为我的双核环境并没有出现一核100%,另一核空闲的情况)。

当CPU整个刷新周期(绝大多数时间)空闲时,CPU占有率趋于0。

书中给出的正弦实现如下:

1 #include "Windows.h"
2 #include "stdlib.h"
3 #include "math.h" 
4 
5 const double SPLIT = 0.01;
6 const int COUNT = 200;
7 const double PI = 3.14159265;
8 const int INTERVAL = 300;
9 
10 int _tmain(int argc, _TCHAR* argv[])
11 {
12     DWORD busySpan[COUNT];  //array of busy times
13     DWORD idleSpan[COUNT];  //array of idle times
14     int half = INTERVAL / 2;
15     double radian = 0.0;
16     for(int i = 0; i < COUNT; i++)
17     {
18         busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));
19         idleSpan[i] = INTERVAL - busySpan[i];
20         radian += SPLIT;
21     }
22     DWORD startTime = 0;
23     int j = 0;
24     while (true)
25     {
26         j = j % COUNT;
27         startTime = GetTickCount();
28         while ((GetTickCount() - startTime) <= busySpan[j]) ;
29         Sleep(idleSpan[j]);
30         j++;
31     }
32     return 0;
33 }


在单核环境(P4 2.40)下,其表现还是不错的:

点击查看大图

在双核环境(Core2 E4500)下,就有点差强人意不尽人意了:

点击查看大图

不过,总还能看出是正弦曲线。

上面两图的问题:

1) 单核时曲线不够平滑,是由于QQ等程序占用CPU所致;

2) 双核时曲线更加抖动,我的理解是除其他程序影响外,由于线程没有固定运行在一个CPU上导致的,后面看到书上提到线程迁移,个人感觉这个叫法欠妥啊,总觉得线程迁移令人费解。

可以立即想到的是:让进程在指定处理器上运行(处理器亲缘关系),由Windows提供了两个API可以做到这一点:GetCurrentProcessSetProcessAffinityMask的。

修改之后的代码如下:

1 #include "Windows.h"
2 #include "stdlib.h"
3 #include "math.h" 
4 
5 const double SPLIT = 0.01;
6 const int COUNT = 200;
7 const double PI = 3.14159265;
8 const int INTERVAL = 300;
9 
10 int _tmain(int argc, _TCHAR* argv[])
11 {
12    SetProcessAffinityMask(
13         GetCurrentProcess(),
14         0x00000001          //cpu mask
15         );
16 
17     DWORD busySpan[COUNT];  //array of busy times
18     DWORD idleSpan[COUNT];  //array of idle times
19     int half = INTERVAL / 2;
20     double radian = 0.0;
21     for(int i = 0; i < COUNT; i++)
22     {
23         busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));
24         idleSpan[i] = INTERVAL - busySpan[i];
25         radian += SPLIT;
26     }
27     DWORD startTime = 0;
28     int j = 0;
29     while (true)
30     {
31         j = j % COUNT;
32         startTime = GetTickCount();
33         while ((GetTickCount() - startTime) <= busySpan[j]) ;
34         Sleep(idleSpan[j]);
35         j++;
36     }
37     return 0;
38 }


双核环境(Core2 E4500)修改之后的输出如下:

点击查看大图

我理想中的表现是:

1) 曲线是平滑的,最好不因其他应用程序或操作的执行而改变;

2) 不管是单核还是双核,峰值皆为100%,谷值为0。

对于第一点,其实就是保证任一刷新周期中的CPU占有率都可以被精确控制在0-100之间,如果你可以使CPU一直保持50%(而不是近似的上下波动),产生一条平滑的曲线就很easy了。

问题的关键在于,除了当前你写的程序可以控制,其他程序或操作如何控制?或者说:如何控制CPU的运行情况才是关键之处

PS: 一晚上老是断网,搞得思路频频被打断,兴致也损了大半。总之,《编程之美》还是值得玩味一把吧:D。

Feedback

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2008-04-17 12:17 by brent
俺也看到这章了,

看到题目的第一反应是设计一个反馈系统。

看到解答挺失望的,感觉有点雕虫小技的味道

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2008-04-17 21:02 by Fox
恩,MS的面试题多如此类,翻一翻还是开阔思路

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2008-04-18 17:15 by Wang Feng
unix上怎么办?

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2008-04-18 18:29 by Fox
@Wang Feng
呵呵,MS的面试题啊:D

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2008-04-20 23:58 by greatws
呵呵,不错。
不过我想到的是直接Hook任务管理器或者Hook查CPU利用率的API,可能会更精确

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2008-04-22 00:38 by www
> 除了当前你写的程序可以控制,其他程序或操作如何控制?

书上也谈到了一些。不过不是控制,而是“适应”。

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2008-06-28 00:40 by 不死阿三
试过了,用c#改写了一下,总是感觉不是很平滑。

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2009-03-29 01:00 by kg
楼主,能不能解释下,为什么要这样设定:
busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));
idleSpan[i] = INTERVAL - busySpan[i];

为什么不可以这样:
busySpan[i] = (DWORD)(1 /2 + (sin(PI * radian) * 1 / 2));
idleSpan[i] = 1- busySpan[i];
这样是不是CPU使用率=1/2*(1+sin(PI * radian))?

liwenge21@sina.com,谢谢回复。

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2010-12-26 16:55 by lateCpp
28 while ((GetTickCount() - startTime) <= busySpan[j]) ;
29 Sleep(idleSpan[j]);

========================
这个可以控制到sleep是正弦曲线吗?
我对WINDOWS API不太熟,大致理解是这样:

如果j=0时,busySpan[0]=150,相应的,IdleSpan[0]=150;
如果j等某值时,busySpan[j]=180,相应的,IdleSpan[j]=120。(由计算公式,busySpan=150*(1+sin(j*pi)),可知,可以取到这个值,起码近似。)

这时候来看while循环(上面所引的行28):
第一次循环:满足<=180条件,执行一次Sleep(120);
第二次循环:依然满足<=180的条件 ,再执行一次Sleep(120);
第三次循环:不满足,跳出;
这样一共Sleep了240秒,显然,这跟原来Sleep(IdleSpan[j])的初衷是不一致的。这个对产生的曲线应该也有影响吧?

不知道理解的对不。希望交流讨论。xueyayang AT gmail DOT com

# re: 编程之美:让CPU占用率曲线听你指挥  回复  更多评论   

2011-01-27 14:08 by BopGroup
《编程之美》这在计划修订改版,欢迎大家提建议,这里(http://bop1.wikispaces.com/)有包含所有题目的wiki,大家可以注册后提问,也可以直接给我发邮件 lispython@gmail.com。

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理