editorloop 占用_如何用一段代码使cpu占用率保持在50%

展开全部

先说说我在没查阅任何资料之前的做法:这里要先明确一点, 任务管理器(taskmgr)里面显62616964757a686964616fe58685e5aeb931333262343136示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是最简单最容易估算的代码段了.接下来是估算: 分析for(i = 0; i < n; i++);可以大致推算出每次循环需要5行汇编代码loop:mov dx iadd dx 1mov i dxcmp i njmp 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的话会造成线程频繁地被唤醒和挂起, 无形中又是增加了内核时间的不确定性影响. 可以得到代码如下:#include int main(){for(;{for(i = 0; i < 9600000; i++);Sleep(10);}return 0;} 编译, 尽量关闭其他应用程序, 停掉能停的服务, 然后运行, 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...)修改程序如下:#include int main(){int i = 96000000, j = 0;LARGE_INTEGER freq = {0};LARGE_INTEGER start = {0};LARGE_INTEGER end = {0};DWORD elapse = 0; QueryPerformanceFrequency(&freq);printf("freq = %u\n", freq.QuadPart);QueryPerformanceCounter(&start);QueryPerformanceCounter(&end);LONGLONG overhead = end.QuadPart - start.QuadPart;printf("overhead = %d\n", overhead); QueryPerformanceCounter(&start);for(i = 0; i < 9600000; i++);QueryPerformanceCounter(&end);LONGLONG temp = (end.QuadPart - start.QuadPart - overhead);printf("temp = %d\n", temp);elapse = (DWORD)(((double)temp / (double)freq.QuadPart) * 1000.0);printf("elapse = %lf\n", temp2); for(;{for(i = 0; i < 9600000; i++);Sleep(elapse);} return 0;} 不过精确计算的结果似乎并不能得到更平滑的图像, 因为我甚至连两次调用api的花费都减掉了, 也许是过犹不及吧...总之我实验的结果是比较粗糙. 最后总结一下两个心得:1. 尽量减少sleep/aweak的频率和花费, 如果频繁发生的话影响会很大.2. 尽量不要调用system call(i/o这些privilege instruction), 因为它会导致很多uncontrolable的kernel time.3. 由于抢先式多任务的争用性质, 想要精确控制曲线几乎是不可能的...(不知这个结论是不是很绝对, 如果有办法用hack的方法hook入taskmgr的函数调用点, 修改它的返回值, 那么想显示什么波形就显示什么波形了, 我是没有功力做这么"伟大"的事情了...)

已赞过

已踩过<

你对这个回答的评价是?

评论

收起

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值