我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:多盈娱乐注册 > 多处理器分配 >

如何用C实现让CPU占用率保持50%(例如用资源管理器查看)

归档日期:07-09       文本归类:多处理器分配      文章编辑:爱尚语录

  可选中1个或多个下面的关键词,搜索相关资料。也可直接点“搜索资料”搜索整个问题。

  这里要先明确一点, 任务管理器(taskmgr)里面显示50%并非意味着当前cpu真就工作在一半的频率下, 仔细观察可以发现, taskmgr的cpu使用率图表是周期性刷新的, 也就是说每经过一个interval期间, cpu活动(执行指令)的时间和空闲(挂起)时间刚好相等的话, 那么就可以认为cpu占用率为50%, 表面上看就可以认为cpu的运行频率降低了(实际上没有).

  有了上面的认识, 就可以得到一个大致的思路: 在程序中首先执行一定规模的代码, 然后调用Sleep()函数让线程挂起X(ms), 如此周而复始, 如果代码执行花费的时间和挂起时间大致相等, 那么从一个interval来看, cpu占用率正好就是50%. 有了这个初步想法, 那么难点就在于如何估算执行代码所花费的时间了, 要执行一定规模的的代码, 最简单的方法就是写一个busy loop(也就是空循环), 比如for(i = 0; i n; i++);如果能合理估算出这段代码的运行时间, 那么就可以知道需要挂起多长时间了.

  之所以选用busy loop而不是其他api之类的, 是因为它不需要调用任何privilege instruction(没有io操作等), 这样就不会在运行中影响内核时间, 因为内核在运行privilege instruction时我们是无法控制的, 要使得50%的曲线更加平滑, 就应该尽量减少uncontrolable的内核时间. 所以busy loop是最简单最容易估算的代码段了.

  jmp loop (很久以前学的汇编, 忘记了那个判断大小后跳转的语句怎么写了...凑合一下吧)

  我的cpu是p4 2.4ghz, 等于2.4 * 10的9次方个时钟周期每秒, 记得以前上微机原理时说现代cpu每个时钟周期可以执行2条以上的代码, 那么我就取平均2条, 于是让 (2400,000,000 * 2) / 5 = 960000000(循环/秒), 也就是说, cpu一秒钟可以运行那个循环960000000次. 不过我们还是不能简单地将n = 960000000, 然后Sleep(1000)了事. 因为windows并不是一个独占的实时系统, 而是一个抢先式多任务系统, 分配给某个线程的运行时间片是会被其他更高优先级的线程抢先的, 而且taskmgr的刷新率也小于1s, 如果我们让cpu工作1s, 挂起1s, 波形很有可能就是呈现锯齿状的, 先达到一个峰值(大于50%), 然后跌到一个很低的占用率! 于是我尝试着降低两个数量级, 令n = 9600000, 而Sleep(10). 用10ms是因为它不大也不小, 1ms的话会造成线程频繁地被唤醒和挂起, 无形中又是增加了内核时间的不确定性影响. 可以得到代码如下:

  编译, 尽量关闭其他应用程序, 停掉能停的服务, 然后运行, Bingo! 在我的机器上, 已经显示cpu在50%附近了, 但是还有小幅度抖动, 此时适当调整n的大小, 几次尝试运行后就可以得到一个相对平滑的结果了!

  为了验证我的推算正确性, 我又找了几台机器试了一下, 发现不同的cpu得到的结果会有所不同, 在一个p4 3.2ghz HT的cpu上, 关掉HT功能, 得到7680000 6ms是比较好的结果, 同时还发现一个好玩的现象, 就是开了HT功能后, 就不用Sleep()函数了, 直接写一个无限循环就可以让cpu跑在50%左右, 这可谓是最简单的方法 :), 而在迅驰和mobile系列cpu上由于运行频率会动态改变, 所以实现起来估算和实际情况出入比较大. 在程序运行起来后, 在taskmgr里面将它的优先级设置为最高(减少争用)也可以一定程度上让波形平滑一些. 而打开内核时间的显示后可以发现, 波形的抖动基本上都是由内核活动影响的.

  根据这个原理, 让cpu波形维持在50%以外的数值也不是不可能的, 关键是让循环次数和挂起时间进行匹配, 而且它们的数量级还与cpu时间片分配和taskmgr波形刷新周期有关. 先估算一个值, 然后根据比例放大或者缩小, 再进行细微调整, 找到两个合适的数值不是难事. 而且, 有了根据这个原理, 还可以让cpu波形呈现sin()函数的形式, 比如先对0~180间取10区间进行取样, 得到一个函数值数组, 然后根据数组的值对busy loop中的循环数和挂起时间进行换算修改, 从理论上来说是这肯定可行的, 不过指令数一多一复杂估算的精确性就会降低.

  为了了解taskmgr的工作原理, 我查阅了一下相应的api资料, 发现两个函数QueryPerformanceFrequency()和QueryPerformanceCounter()可以精确得到一段代码的运行时间, 于是想到可以构造两个相同的大busy loop, 先在程序开头算出运行所需的精确时间, 然后用它来对Sleep()函数进行赋值, 这样也可以实现某种意义上的自动化(不用估算了XD...)

  不过精确计算的结果似乎并不能得到更平滑的图像, 因为我甚至连两次调用api的花费都减掉了, 也许是过犹不及吧...总之我实验的结果是比较粗糙.

  3. 由于抢先式多任务的争用性质, 想要精确控制曲线几乎是不可能的...

  (不知这个结论是不是很绝对, 如果有办法用hack的方法hook入taskmgr的函数调用点, 修改它的返回值, 那么想显示什么波形就显示什么波形了, 我是没有功力做这么伟大的事情了...)

本文链接:http://cakesbyrita.net/duochuliqifenpei/864.html