讓多核CPU佔用率曲線聽你指揮(Windows實現)——《編程之美》1.1學習筆記

來源:互聯網
上載者:User
讓多核CPU佔用率曲線聽你指揮——《編程之美》1.1學習筆記Problem:   

寫一個程式,讓使用者來決定Windows工作管理員(Task Manager)的CPU佔用率。有以下幾種情況:

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

2.CPU的佔用率為一條直線,具體佔用率由命令列參數決定(範圍1~100);

3.CPU的佔用率狀態為一條正弦曲線。

分析與解法:

(1)通過觀察工作管理員,它大約1s更新一次。當CPU使用率為0時,System Idle Process佔用了CPU的空閑時間。

System Idle Process在CPU閒置的時候,發出一個IDLE命令,使CPU掛起(暫時停止工作),可有效降低CPU核心的溫度,無法終止。在這個進程裡出現的CPU佔用數值並不是真正的佔用而是體現的CPU的空閑率,也就說這個數值越大CPU的空閑率就越高,反之就是CPU的佔用率越高。Linux中對應的進程為init,PID為1。

 

當系統中的進程或者在等待使用者輸入,或者在等待某些事件的發生(發出I/O請求等待I/O響應),或者主動進入休眠狀態(比如Sleep())。

在工作管理員中的一個重新整理周期內,CPU忙(執行應用程式)的時間和重新整理周期總時間的比率就是CPU的佔用率。其顯示的是每個重新整理周期內CPU佔用率的統計平均值。我們可以寫一個程式讓它在工作管理員的重新整理時間內一會兒忙,一會兒閑,通過調節忙/閑的比例,來控制工作管理員中顯示的CPU佔用率。

 

書上的代碼以單核CPU為前提,但對於多核CPU來說,同一個進程可能被CPU的任務分配器分配到不同的核心上執行,所以造成無法讓工作管理員達到預想的效果。其實開啟工作管理員,可以看到多個CPU使用記錄。本人電腦CPU是Core i5 450M,雙核4線程。在OS看來就如同有四個CPU工作一樣。我的工作管理器中就有四個CPU使用記錄。

所謂超執行緒技術就是利用特殊的硬體指令,把多執行緒器內部的兩個邏輯核心類比成兩個物理晶片,從而使單個處理器就能“享用”線程級的並行計算的處理器技術。多線程技術可以在支援多線程的作業系統和軟體上,有效增強處理器在多任務、多執行緒上的處理能力。

 

可以使用SetProcessAffinityMask()函數可以使特定的處理器運行指定進程。

BOOL SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask);

第一個參數用來指定指定哪個進程,傳入它的控制代碼。第二個進程用來指定哪個CPU核心來執行此進程。

DWORD_PTR,其實就是unsigned long*.Unsigned long type for pointer precision.Use when casting a pointer to a long type to perform pointer arithmetic.(Also commonly used for general 32-bit parameters that have been extended to 64 bits in 64-bit windows.)

DWORD 其實就是unsigned long。Windows下常用來儲存地址或存放指標。

比如這樣調用函數:

::SetProcessAffinityMask(::GetCurrentProcess(),0x1);可以指定當前執行的進程在第一個CPU上運行(00000001)。對於雙核CPU,

::SetProcessAffinityMask(::GetCurrentProcess(),0x2);可以指定在第二個CPU上運行。(00000010)

::SetProcessAffinityMask(::GetCurrentProcess(),0x3);可以允許在兩個CPU上任意運行。(000000011)

        ::SetProcessAffinityMask(::GetCurrentProcess(),0x3);可以允許在第三個CPU上任意運行。(000000100)

       ::SetProcessAffinityMask(::GetCurrentProcess(),0x3);可以允許在第一個和第三個CPU上任意運行。(00000101)

 

        以此類推。。。

 

 

HANDLE GetCurrentProcess(void);

可以獲得當前進程的控制代碼。注意,這個控制代碼為一個偽控制代碼。只能在我們的進程中才能代表當前進程的控制代碼,事實上這個函數目前只是簡單的返回-1這個值。也就是說在我們的程式中-1便能表示本進程的控制代碼。

(2)那麼對於繪製50%直線,程式碼為:

 

 

#include <Windows.h><br />#include<stdlib.h><br />#include<tchar.h><br />int _tmain(int argc,_TCHAR* argv[])<br />{<br />int busyTime = 10;<br />int idleTime = busyTime*5;<br />__int64 startTime = 0;<br />::SetThreadAffinityMask(::GetCurrentProcess(),0x00000001);<br />while(true)<br />{<br />startTime = GetTickCount();<br /> //busy loop<br />while((GetTickCount() - startTime) <= busyTime);<br /> //idle loop<br /> Sleep(idleTime);<br />}<br />return 0;<br />}

GetTickCount()可以得到系統從啟動到運行到現在所經曆時間的毫秒值。最多能統計到49.7天。我們利用它判斷busy loop要持續多久。

其中idleTime為busyTime的五倍,可以修改其值使其更逼近50%。不同機子的情況不同。

__int64是VC++的64位擴充。範圍為[-2^63,2^63)。當64位與32位混合運算時,32位整數會隱式轉換成64位整數。輸入輸出它時使用cin、cout會造成錯誤。需要使用scanf("%I64d",&a);和printf("%I64d",a);

還有unsigned __int64,其範圍為[0,2^64)。

對應g++中的64位擴充為long long和unsigned long long。範圍與運算與上相仿。輸入輸出使用scanf("%lld",&a);和printf("%lld",a);

 

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

_tmain這個符號多見於VC++建立的控制台工程中,這個是為了保證移植unicode而加入的(一般_t、_T、T()這些東西都和unicode有關係)。定義在標頭檔tchar.h中。

(3)對於繪製正弦曲線:

 

#include <Windows.h><br />#include<stdlib.h><br />#include<math.h><br />#include<tchar.h><br />const double SPLIT = 0.01;<br />const int COUNT = 200;<br />const double PI = 3.14159265;<br />const int INTERVAL = 300;<br />int _tmain(int argc, _TCHAR* argv[] )<br />{<br />DWORD busySpan[COUNT]; //array of busy times<br />DWORD idleSpan[COUNT]; //array of idle times<br />int half = INTERVAL/2;<br />double radian = 0.0;<br /> //如何近似趨近一條正弦曲線?這樣!<br />for(int i = 0; i < COUNT; ++i)<br />{<br />busySpan[i] = (DWORD)(half + (sin(PI * radian) * half));<br />idleSpan[i] = INTERVAL - busySpan[i];<br />radian += SPLIT;<br />}<br />DWORD startTime = 0;<br />int j = 0;<br />::SetProcessAffinityMask(::GetCurrentProcess(),0x00000002);<br />while(true)<br />{<br />j = j % COUNT;<br />startTime = GetTickCount();<br />while((GetTickCount() - startTime) <= busySpan[j]);<br />Sleep(idleSpan[j]);<br />j++;<br />}<br />return 0;<br />}

通過在一個周期2*PI中等分200份,將每一個間隔點的half + (sin( PI * radian) * half))的值存入busySpan[i],將其補植存入idleSpan[i]。half是整個範圍INTERVAL的一半。這樣可以近似趨近一條正弦曲線。

運行效果為:

(4)可以通過RDTSC指令獲得當前CPU核心運行周期數。

在x86平台上定義函數:

inline __int64 GetCPUTickCount()<br />{<br />__asm<br />{<br />rdtsc;<br />}<br />}

在x64平台上定義:

#define GetCPUTickCount() __rdtsc()

使用CallNtPowerInformation API得到CPU頻率,從而將周期數轉化為毫秒數,例如如下:

_PROCESSOR_POWER_INFORMATION info;<br />CallNTPowerInformation(11,//query processor power information<br />NULL,//no input buffer<br />0,//input buffer size is zero<br />&info,//output buffer<br />sizeof(info)//outbuf size<br />);<br />__int64 t_begin = GetCPUTickCount();<br />//do something<br />__int64 t_end = GetCPUTickCount();<br />millisec = ((double)t_end - (double)t_begin)/(double)info.CurrentMhz; 

 

RDTSC指令讀取當前CPU的周期數,在多CPU系統中這個周期數在不同的CPU間基數不同,頻率也不同。用從兩個不同的CPU得到的周期數來計算會得出沒有意義的值。所以需要用SetProcessAffinityMask避免進程遷移。另外,CPU的頻率也會隨系統供電及負荷情況有所調整。
More:

如何在Linux下如Ubuntu中的系統監視器中實現繪製正弦曲線?請聽下回分解。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.