让CPU的占有率曲线听我指挥

最近我要在公司的一个study group负责AWS的AutoScaling功能的介绍。AWS可以根据instance(虚拟机)的CPU使用量进行scaling。

为了做demo,于是就有这样一个需求:让instance上的CPU听我指挥,当然简单的方法就是写一个死循环,让CPU 100%。但如果make things more interesting,希望实现CPU在某个范围内变化又要怎么做哩?

之前看过,邹欣大神的书《编程之美》,其中第一个问题就是“让CPU占有率曲线听你指挥”,里面提到了一些解法,更有甚者,做到了能让CPU占有率曲线按正弦函数波动。我和同事大神Jason san中午吃饭时聊了这个需求,想不到他下午就分别用C++和Python实现了一种动态适应的解决方法。以下我们就来讨论下这个有趣问题的解法:

首先书上提到一个简单方法让CPU维持在50%:

让CPU在一段时间内(根据Task Manager的采样率)跑busy和idle两个不同的循环,从而通过不同的时间比例,来调节CPU使用率。

那么对于一个空循环

for(i = 0; i < n; i++);

又该如何来估算这个最合适的n的值呢?首先我们把这个空循环简单写成汇编代码(伪代码):

1 next:
2 mov eax, dword ptr[i]; i放入寄存器
3 add exa, 1; 寄存器加1
4 mov dword ptr [i], eax; 寄存器赋回i
5 cmp eax, dword ptr[n]; 比较i和n
6 jl next; i小于n时重复循环

假设这段代码要运行的CPU是P4,主频是2.4Ghz(2.4*10的9次方个时钟周期每秒)。现代CPU每个时钟周期可以执行两条以上的代码,我们取平均值两条,于是有

(2 400 000 000*2)/5 = 960 000 000 (循环/秒)(这边除以5是因为上面汇编代码有5条汇编语句,尼玛书上没有说清楚,让我这样的小白想了好久……),也就是说CPU每一秒钟可以运行这个空循环960 000 000次。不过我们还是不能简单的将n=960 000 000,然后Sleep(1000)了事。如果我们让CPU工作1秒钟,然后休息1秒钟,波形很有可能就是锯齿状的一样先到达峰值(>50%),然后跌倒一个很低的占用率。

我们尝试着降低两个数量级,令n = 9 600 000,而睡眠时间则相应的改为10毫秒。代码清单如下:

int main()
{
    for(; ;)
    {
        for(int i = 0; i < 9600000; i++)
            ;
        sleep(10)
    }
    return 0;
}

再不断调整参数后,就能得到一条大致稳定的CPU占有率直线。

但是这个方法最大的问题是,参数都是根据特定机器算出来的。在其他机器上要重新调整,所以需要有更加智能的方法。

之后书中给出了另外两个解法:

解法二:使用了系统API:GetTickCount()和Sleep()

解法三:使用工具Perfmon.exe

具体我就不列出来了,大家有兴趣去看书吧。(我这样给这本书打广告了,大神不会告我侵权了吧……^_^)

但是,这些方法都没有考虑到多核和多CPU的情况,书中提到对于多CPU的问题首先需要获得系统的CPU信息。可以使用GetProcessorInfo()获得多处理器的信息,然后指定进程在哪一个处理器上运行。其中指定运行使用的是SetThreadAffinityMask()函数。

另外,还可以使用RDTSC指令获取当前CPU核心运行周期数。

在x86平台定义函数:

inline unsigned_int64 GetCPUTickCount()
{
    _asm
    {
        rdtsc;
    }
}

在x64平台上定义:

  #define GetCPUTickCount()_rdtsc()

使用CallNtPowerInformation API得到CPU频率,从而将周期数转化为毫秒数。(这边这段有点不知所云了……)

这边我给出两段代码,分别用C++和Python实现,通过动态获取CPU的状态,调整线程的数量来实现让CPU保持在某一个值,且考虑了多CPU情况。

  1 #define _WIN32_WINNT 0x0502
  2
  3 #include <cstdio>
  4 #include <cstdlib>
  5 #include <ctime>
  6
  7 #include <Windows.h>
  8 #include <process.h>
  9
 10 #define TARGET_CPU_RATE (80.0)
 11
 12 extern "C" {
 13     typedef struct _CONTROL_PARAM
 14     {
 15         volatile LONG m_exit;
 16         volatile LONGLONG m_rest;
 17     } CONTROL_PARAM;
 18 };
 19
 20 static CONTROL_PARAM g_param;
 21
 22 unsigned __stdcall task_thread(void *pparam)
 23 {
 24     if (!pparam)
 25         return 0;
 26
 27     CONTROL_PARAM *pctrl = (CONTROL_PARAM *)pparam;
 28
 29     LONGLONG rest64 = 0;
 30     while (true)
 31     {
 32         if (rest64 > pctrl->m_rest)
 33         {
 34             Sleep(1);
 35             rest64=0;
 36         }
 37         else
 38             rest64++;
 39     }
 40
 41     return 0;
 42 }
 43
 44 inline unsigned __int64 u64_time(FILETIME &_ft)
 45 {
 46     unsigned __int64 u64;
 47     u64 = _ft.dwHighDateTime;
 48     u64 = u64 << 32;
 49     u64 |= _ft.dwLowDateTime;
 50     return u64;
 51 }
 52
 53 int main()
 54 {
 55     SYSTEM_INFO sys_info;
 56     ZeroMemory(&sys_info, sizeof(SYSTEM_INFO));
 57
 58     GetSystemInfo(&sys_info);
 59     int cpu_cnt = (int)sys_info.dwNumberOfProcessors;
 60
 61     if (0 == cpu_cnt)
 62         cpu_cnt = 1;
 63     printf("cpu count: %d\n", cpu_cnt);
 64
 65     g_param.m_rest = (DWORD)-1;
 66     for (int i=0; i<cpu_cnt; ++i)
 67     {
 68         _beginthreadex(NULL,0,task_thread,&g_param,0,NULL);
 69     }
 70
 71     FILETIME idleTime;
 72     FILETIME kernelTime;
 73     FILETIME userTime;
 74
 75     FILETIME last_idleTime;
 76     FILETIME last_kernelTime;
 77     FILETIME last_userTime;
 78
 79     bool initialized = false;
 80
 81     while (true)
 82     {
 83         if (GetSystemTimes(&idleTime,&kernelTime,&userTime))
 84         {
 85             if (initialized)
 86             {
 87                 unsigned __int64 usr = u64_time(userTime) - u64_time(last_userTime);
 88                 unsigned __int64 ker = u64_time(kernelTime) - u64_time(last_kernelTime);
 89                 unsigned __int64 idl = u64_time(idleTime) - u64_time(last_idleTime);
 90
 91                 double sys = ker + usr;
 92                 double cpu = (sys - (double)idl) / sys * 100.0;
 93
 94                 double dif = TARGET_CPU_RATE - cpu;
 95                 g_param.m_rest = (LONGLONG)((double)g_param.m_rest * (1.0 + dif/100.0));
 96
 97                 printf("rest = %I64d, cpu = %d\n", g_param.m_rest, (int)cpu);
 98             }
 99             else
100                 initialized = true;
101
102             last_idleTime = idleTime;
103             last_kernelTime = kernelTime;
104             last_userTime = userTime;
105         }
106
107         Sleep(300);
108     }
109
110     return getchar();
111 }

Python程序:

 1 from ctypes import windll, Structure, byref, c_longlong
 2 from ctypes.wintypes import DWORD
 3
 4 import win32api
 5 import multiprocessing
 6
 7 WinGetSystemTimes = windll.kernel32.GetSystemTimes
 8
 9 class FILETIME(Structure):
10     _fields_ = [ ("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD) ]
11
12 def pyGetSystemTimes():
13     idleTime, kernelTime, userTime = FILETIME(), FILETIME(), FILETIME()
14     WinGetSystemTimes(byref(idleTime), byref(kernelTime), byref(userTime))
15     return (idleTime, kernelTime, userTime)
16
17 def longTime(ft):
18     tm = ft.dwHighDateTime
19     tm = tm << 32
20     tm = tm | ft.dwLowDateTime
21     return tm
22
23 TARGET_CPU_RATE = 70.0
24
25 def worker(val):
26     rest = 0
27     while (True):
28         if rest > val.value:
29             rest = 0
30             win32api.Sleep(1)
31         else:
32             rest += 1
33
34 if __name__ == ‘__main__‘:
35     sys_info = win32api.GetSystemInfo()
36     cpu_cnt = sys_info[5]
37
38     val = multiprocessing.Value(c_longlong, 100, lock=False)
39     print type(val.value)
40
41     threads = []
42     for i in range(cpu_cnt):
43         p = multiprocessing.Process(target=worker, args=(val,))
44         p.start()
45         threads.append(p)
46
47     initialized = False
48     last_times = (FILETIME(), FILETIME(), FILETIME())
49     while (True):
50         cur_times = pyGetSystemTimes()
51
52         if initialized:
53             usr = longTime(cur_times[2]) - longTime(last_times[2])
54             ker = longTime(cur_times[1]) - longTime(last_times[1])
55             idl = longTime(cur_times[0]) - longTime(last_times[0])
56
57             sys = float(ker + usr)
58             cpu = (sys - float(idl)) / sys * 100.0;
59
60             dif = TARGET_CPU_RATE - cpu;
61             val.value = long(float(val.value) * (1.0 + dif / 100.0));
62
63             print "rest=", val.value, ", cpu=", int(cpu)
64         else:
65             initialized = True
66
67         last_times = cur_times
68         win32api.Sleep(300)
时间: 2024-08-05 19:27:50

让CPU的占有率曲线听我指挥的相关文章

1.1 让CPU占用率曲线听你指挥[cpu manager]

[本文链接] http://www.cnblogs.com/hellogiser/p/cpu-manager.html [题目] 写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率.程序越精简越好,计算机语言不限.例如,可以实现下面三种情况: 1.    CPU的占用率固定在50%,为一条直线: 2.    CPU的占用率为一条直线,但是具体占用率由命令行参数决定(参数范围1~ 100): 3.    CPU的占用率状态是一个正弦曲线. [分析] 如果不考

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

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

编程之美读书笔记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

生成CPU使用率 sin 曲线 控制cpu使用率 编程之美

入职Oracle 以后想着把之前写过的<编程之美>中控制CPU使用率曲线的程序再写一边, 但是总是因为入职需要学习的东西太多, 没有时间. 程序早就写好了, 终于有机会贴出来了.o(∩∩)o.. 最早的时候我采用C实现的效果并不好, 当时也没有认真调试, 最初的时候采用C#实现的效果反而不错, 因为C#有很多方便的类库可以获取CPU的占用率, 控制sleep时间等等.其实在C中也很容易实现. 总体的算法思想, 这里就不再重复了, 可以参考下面的链接 : http://blog.csdn.net

线上问题:如何定位解决CPU高占有率

(原文转自:http://www.blogjava.net/hankchen) 以我们最近出现的一个实际故障为例,介绍怎么定位和解决这类问题. 根据top命令,发现PID为28555的Java进程占用CPU高达200%,出现故障. 通过ps aux | grep PID命令,可以进一步确定是tomcat进程出现了问题.但是,怎么定位到具体线程或者代码呢? 首先显示线程列表: ps -mp pid -o THREAD,tid,time 找到了耗时最高的线程28802,占用CPU时间快两个小时了!

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

问题: 写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率(单核).有以下几种情况: 1.CPU占用率固定在50%,为一条直线 2.CPU的占用率为一条直线,具体占用率由命令行参数决定(范围1~100) 3.CPU的占用率状态为一条正弦曲线 4.多核处理器情况下上述问题怎么解决 分析与解答 首先确定CPU占用率的定义,即在任务管理器的一个刷新周期内,CPU忙(执行应用程序)的时间和刷新周期总时间的比率,就是CPU的占用率,也可以说成,任务管理器中显示的是每

玩转CPU运行曲线

Leaf 是不是从来没有想过看看cpu运行曲线啊骚年?顶多也就仅仅是看看cpu利用率,吓自己一跳后感觉关闭几个不该打开的程序~ 然而问题来了,微软公司要让你绘制cpu运行曲线啊!!不仅是固定的直线,还要绝对值函数,还有正弦!!我的天这游戏还能玩? Require 写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率.程序越简越好,计算机语言不限.例如,可以实现下面三种情况: CPU的占用率固定在50%,为一条直线 CPI的占用率为一条直线,但是具体占用率由命