zoukankan      html  css  js  c++  java
  • 让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
    ", 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
    ", 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)
  • 相关阅读:
    关于celery踩坑
    关于git的分批提交pull requests流程
    SymGAN—Exploiting Images for Video Recognition: Heterogeneous Feature Augmentation via Symmetric Adversarial Learning学习笔记
    AFN—Larger Norm More Transferable: An Adaptive Feature Norm Approach for Unsupervised Domain Adaptation学习笔记
    Learning to Transfer Examples for Partial Domain Adaptation学习笔记
    Partial Adversarial Domain Adaptation学习笔记
    Partial Transfer Learning with Selective Adversarial Networks学习笔记
    Importance Weighted Adversarial Nets for Partial Domain Adaptation学习笔记
    Exploiting Images for Video Recognition with Hierarchical Generative Adversarial Networks学习笔记
    improved open set domain adaptation with backpropagation 学习笔记
  • 原文地址:https://www.cnblogs.com/randyyang/p/4115570.html
Copyright © 2011-2022 走看看