小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

讓CPU占用率曲線聽你指揮

 昵稱57922 2008-03-21

寫一個程序,讓用戶來決定Windows任務管理器(Task Manager)的CPU占用率。程序越精簡越好,計算機語言不限。例如,可以實現(xiàn)下面三種情況:

1.    CPU的占用率固定在50%,為一條直線;

2.    CPU的占用率為一條直線,但是具體占用率由命令行參數(shù)決定(參數(shù)范圍1~ 100);

3.    CPU的占用率狀態(tài)是一個正弦曲線。

分析與解法

有一名學生寫了如下的代碼:

while (true)
{
   if (busy)
       i++;
   else
}

然后她就陷入了苦苦思索:else干什么呢?怎么才能讓電腦不做事情呢?CPU使用率為0的時候,到底是什么東西在用CPU?另一名學生花了很多時間構(gòu)想如何“深入內(nèi)核,以控制CPU占用率”——可是事情真的有這么復雜么?

MSRA TTG(Microsoft Research Asia, Technology Transfer Group)的一些實習生寫了各種解法,他們寫的簡單程序可以達到如圖1-1所示的效果。

圖1-1  編碼控制CPU占用率呈現(xiàn)正弦曲線形態(tài)

看來這并不是不可能完成的任務。讓我們仔細地回想一下寫程序時曾經(jīng)碰到的問題,如果我們不小心寫了一個死循環(huán),CPU占用率就會跳到最高,并且一直保持100%。我們也可以打開任務管理器,實際觀測一下它是怎樣變動的。憑肉眼觀察,它大約是1秒鐘更新一次。一般情況下,CPU使用率會很低。但是,當用戶運行一個程序,執(zhí)行一些復雜操作的時候,CPU的使用率會急劇升高。當用戶晃動鼠標時,CPU的使用率也有小幅度的變化。

那當任務管理器報告CPU使用率為0的時候,誰在使用CPU呢?通過任務管理器的“進程(Process)”一欄可以看到,System Idle Process占用了CPU空閑的時間——這時候大家該回憶起在“操作系統(tǒng)原理”這門課上學到的一些知識了吧。系統(tǒng)中有那么多進程,它們什么時候能“閑下來”呢?答案很簡單,這些程序或者在等待用戶的輸入,或者在等待某些事件的發(fā)生(WaitForSingleObject()),或者進入休眠狀態(tài)(通過Sleep()來實現(xiàn))。

在任務管理器的一個刷新周期內(nèi),CPU忙(執(zhí)行應用程序)的時間和刷新周期總時間的比率,就是CPU的占用率,也就是說,任務管理器中顯示的是每個刷新周期內(nèi)CPU占用率的統(tǒng)計平均值。因此,我們寫一個程序,讓它在任務管理器的刷新期間內(nèi)一會兒忙,一會兒閑,然后通過調(diào)節(jié)忙/閑的比例,就可以控制任務管理器中顯示的CPU占用率。

【解法一】簡單的解法

步驟1    要操縱CPU的usage曲線,就需要使CPU在一段時間內(nèi)(根據(jù)Task        Manager的采樣率)跑busy和idle兩個不同的loop,從而通過不同的時間      比例,來獲得調(diào)節(jié)CPU Usage的效果。

步驟2    Busy loop可以通過執(zhí)行空循環(huán)來實現(xiàn),idle可以通過Sleep()來實現(xiàn)。

問題的關(guān)鍵在于如何控制兩個loop的時間,方法有二:

Sleep一段時間,然后以for循環(huán)n次,估算n的值。

那么對于一個空循環(huán)for(i = 0; i < n; i++);又該如何來估算這個最合適的n值呢?我們都知道CPU執(zhí)行的是機器指令,而最接近于機器指令的語言是匯編語言,所以我們可以先把這個空循環(huán)簡單地寫成如下匯編代碼后再進行分析:

loop:

mov dx i         ;將i置入dx寄存器

inc dx           ;將dx寄存器加1

mov i dx         ;將dx中的值賦回i

cmp i n          ;比較i和n

jl loop          ;i小于n時則重復循環(huán)

假設這段代碼要運行的CPU是P4 2.4Ghz(2.4 * 10的9次方個時鐘周期每秒)。現(xiàn)代CPU每個時鐘周期可以執(zhí)行兩條以上的代碼,那么我們就取平均值兩條,于是讓(2 400 000 000 * 2)/5=960 000 000(循環(huán)/秒),也就是說CPU 1秒鐘可以運行這個空循環(huán)960 000 000次。不過我們還是不能簡單地將n = 60 000 000,然后Sleep(1000)了事。如果我們讓CPU工作1秒鐘,然后休息1秒鐘,波形很有可能就是鋸齒狀的——先達到一個峰值(大于>50%),然后跌到一個很低的占用率。

我們嘗試著降低兩個數(shù)量級,令n = 9 600 000,而睡眠時間相應改為10毫秒(Sleep(10))。用10毫秒是因為它不大也不小,比較接近Windows的調(diào)度時間片。如果選得太?。ū热?毫秒),則會造成線程頻繁地被喚醒和掛起,無形中又增加了內(nèi)核時間的不確定性影響。最后我們可以得到如下代碼:

代碼清單1-1

int main()

{

    for(;;)

    {

        for(int i = 0; i < 9600000; i++);

        Sleep(10);

    }

    return 0;

}

在不斷調(diào)整9 600 000的參數(shù)后,我們就可以在一臺指定的機器上獲得一條大致穩(wěn)定的50% CPU占用率直線。

使用這種方法要注意兩點影響:

1.    盡量減少sleep/awake的頻率,如果頻繁發(fā)生,影響則會很大,因為此時優(yōu)先級更高的操作系統(tǒng)內(nèi)核調(diào)度程序會占用很多CPU運算時間。

2.    盡量不要調(diào)用system call(比如I/O這些privilege instruction),因為它也會導致很多不可控的內(nèi)核運行時間。

該方法的缺點也很明顯:不能適應機器差異性。一旦換了一個CPU,我們又得重新估算n值。有沒有辦法動態(tài)地了解CPU的運算能力,然后自動調(diào)節(jié)忙/閑的時間比呢?請看下一個解法。

【解法二】使用GetTickCount()和Sleep()

我們知道GetTickCount()可以得到“系統(tǒng)啟動到現(xiàn)在”的毫秒值,最多能夠統(tǒng)計到49.7天。另外,利用Sleep()函數(shù),最多也只能精確到1毫秒。因此,可以在“毫秒”這個量級做操作和比較。具體如下:

利用GetTickCount()來實現(xiàn)busy loop的循環(huán),用Sleep()實現(xiàn)idle loop。偽代碼如下:

代碼清單1-2

int busyTime = 10;  //10 ms

int idleTime = busyTime;  //same ratio will lead to 50% cpu usage

Int64 startTime = 0;       

while (true)

{

startTime = GetTickCount();

// busy loop的循環(huán)

while ((GetTickCount() - startTime) <= busyTime) ;

    //idle loop

    Sleep(idleTime);

}

這兩種解法都是假設目前系統(tǒng)上只有當前程序在運行,但實際上,操作系統(tǒng)中有很多程序都會在不同時間執(zhí)行各種各樣的任務,如果此刻其他進程使用了10% 的CPU,那我們的程序應該只能使用40%的CPU(而不是機械地占用50%),這樣可達到50%的效果。

怎么做呢?

我們得知道“當前CPU占用率是多少”,這就要用到另一個工具來幫忙——Perfmon.exe。

Perfmon是從Windows NT開始就包含在Windows服務器和臺式機操作系統(tǒng)的管理工具組中的專業(yè)監(jiān)視工具之一(如圖1-2所示)。Perfmon可監(jiān)視各類系統(tǒng)計數(shù)器,獲取有關(guān)操作系統(tǒng)、應用程序和硬件的統(tǒng)計數(shù)字。Perfmon的用法相當直接,只要選擇您所要監(jiān)視的對象(比如:處理器、RAM或硬盤),然后選擇所要監(jiān)視的計數(shù)器(比如監(jiān)視物理磁盤對象時的平均隊列長度)即可。還可以選擇所要監(jiān)視的實例,比如面對一臺多CPU服務器時,可以選擇監(jiān)視特定的處理器。

Figure 1: System Monitor (Perfmon)

圖1-2  系統(tǒng)監(jiān)視器(Perfmon)

我們可以寫程序來查詢Perfmon的值,Microsoft .Net Framework提供了PerformanceCounter()這一類型,從而可以方便地拿到當前各種計算機性能數(shù)據(jù),包括CPU的使用率。例如下面這個程序——

【解法三】能動態(tài)適應的解法

代碼清單1-3

//C# code  

static void MakeUsage(float level)

{

   PerformanceCounter p = new PerformanceCounter("Processor", "% Processor Time", "_Total");

   while (true)

   {

       if (p.NextValue() > level)

           System.Threading.Thread.Sleep(10);

    }

}

可以看到,上面的解法能方便地處理各種CPU使用率參數(shù)。這個程序可以解答前面提到的問題2。

有了前面的積累,我們應該可以讓任務管理器畫出優(yōu)美的正弦曲線了,見下面的代碼。

【解法四】正弦曲線

代碼清單1-4

//C++ code  to make task manager generate sine graph

#include "Windows.h"

#include "stdlib.h"

#include "math.h"

const double SPLIT = 0.01;

const int COUNT = 200;

const double PI = 3.14159265;

const int INTERVAL = 300;

int _tmain(int argc, _TCHAR* argv[])

{

    DWORD busySpan[COUNT];  //array of busy times

    DWORD idleSpan[COUNT];  //array of idle times

    int half = INTERVAL / 2;

    double radian = 0.0;

    for(int i = 0; i < COUNT; i++)

    {

        busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));

        idleSpan[i] = INTERVAL - busySpan[i];

        radian += SPLIT;

    }

    DWORD startTime = 0;       

    int j = 0;

    while (true)

    {

        j = j % COUNT;

        startTime = GetTickCount();

        while ((GetTickCount() - startTime) <= busySpan[j]) ;

        Sleep(idleSpan[j]);

        j++;

    }

    return 0;

}

討論

如果機器是多CPU,上面的程序會出現(xiàn)什么結(jié)果?如何在多個CPU時顯示同樣的狀態(tài)?例如,在雙核的機器上,如果讓一個單線程的程序死循環(huán),能讓兩個CPU的使用率達到50%的水平么?為什么?

多CPU的問題首先需要獲得系統(tǒng)的CPU信息??梢允褂肎etProcessorInfo()獲得多處理器的信息,然后指定進程在哪一個處理器上運行。其中指定運行使用的是SetThreadAffinityMask()函數(shù)。

另外,還可以使用RDTSC指令獲取當前CPU核心運行周期數(shù)。

在x86平臺上定義函數(shù):

inline __int64 GetCPUTickCount()

{

     __asm

     {

         rdtsc;

     }

}

在x64平臺上定義:

#define GetCPUTickCount() __rdtsc()

使用CallNtPowerInformation API得到CPU頻率,從而將周期數(shù)轉(zhuǎn)化為毫秒數(shù),例如:

代碼清單1-5

_PROCESSOR_POWER_INFORMATION info;

CallNTPowerInformation(11,   //query processor power information

    NULL,                 //no input buffer

    0,                    //input buffer size is zero

    &info,               //output buffer

    Sizeof(info));     //outbuf size

    __int64 t_begin = GetCPUTickCount();

    //do something

    __int64 t_end = GetCPUTickCount();

    double millisec = ((double)t_end –

        (double)t_begin)/(double)info.CurrentMhz;

RDTSC指令讀取當前CPU的周期數(shù),在多CPU系統(tǒng)中,這個周期數(shù)在不同的CPU之間基數(shù)不同,頻率也有可能不同。用從兩個不同的CPU得到的周期數(shù)作計算會得出沒有意義的值。如果線程在運行中被調(diào)度到了不同的CPU,就會出現(xiàn)上述情況??捎肧etThreadAffinityMask避免線程遷移。另外,CPU的頻率會隨系統(tǒng)供電及負荷情況有所調(diào)整。

總結(jié)

能幫助你了解當前線程/進程/系統(tǒng)效能的API大致有以下這些:

1.     Sleep()——這個方法能讓當前線程“停”下來。

2.     WaitForSingleObject()——自己停下來,等待某個事件發(fā)生

3.     GetTickCount()——有人把Tick翻譯成“嘀嗒”,很形象。

4.     QueryPerformanceFrequency()、QueryPerformanceCounter()——讓你訪問到精度更高的CPU數(shù)據(jù)。

5.     timeGetSystemTime()——是另一個得到高精度時間的方法。

6.     PerformanceCounter——效能計數(shù)器。

7.     GetProcessorInfo()/SetThreadAffinityMask()。遇到多核的問題怎么辦呢?這兩個方法能夠幫你更好地控制CPU。

8.     GetCPUTickCount()。想拿到CPU核心運行周期數(shù)嗎?用用這個方法吧。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多