如何用C实现让CPU占用率保持50%(例如用资源管理器查看)
如何用C实现让CPU占用率保持50%(例如用资源管理器查看)如何用C实现让CPU占用率成正弦曲线...
如何用C实现让CPU占用率保持50%(例如用资源管理器查看)
如何用C实现让CPU占用率成正弦曲线 展开
如何用C实现让CPU占用率成正弦曲线 展开
2013-07-25
展开全部
先说说我在没查阅任何资料之前的做法:
这里要先明确一点, 任务管理器(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是最简单最容易估算的代码段了.
接下来是估算: 分析for(i = 0; i < n; i++);可以大致推算出每次循环需要5行汇编代码
loop:
mov dx i
add dx 1
mov i dx
cmp i n
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的话会造成线程频繁地被唤醒和挂起, 无形中又是增加了内核时间的不确定性影响. 可以得到代码如下:
#include <stdio.h>
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 <stdio.h>
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的函数调用点, 修改它的返回值, 那么想显示什么波形就显示什么波形了, 我是没有功力做这么"伟大"的事情了...)
这里要先明确一点, 任务管理器(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是最简单最容易估算的代码段了.
接下来是估算: 分析for(i = 0; i < n; i++);可以大致推算出每次循环需要5行汇编代码
loop:
mov dx i
add dx 1
mov i dx
cmp i n
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的话会造成线程频繁地被唤醒和挂起, 无形中又是增加了内核时间的不确定性影响. 可以得到代码如下:
#include <stdio.h>
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 <stdio.h>
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的函数调用点, 修改它的返回值, 那么想显示什么波形就显示什么波形了, 我是没有功力做这么"伟大"的事情了...)
2013-07-25
展开全部
用相当多的嵌套循环。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
2013-07-25
展开全部
呵呵
问题相当有创意
学习
问题相当有创意
学习
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询