首页 > 代码库 > 编程之美读书笔记1.1——让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的使用率达到50%的水平么?为什么?多CPU的问题首先须要获得系统的CPU信息。能够使用GetProcessorInfo()获得多处理器的信息,然后指定进程在哪一个处理器上执行。
当中指定执行使用的是SetThreadAffinityMask()函数。
书中提到两个重要的API,GetProcessorInfo()与SetThreadAffinityMask()。经查询MSDN。GetProcessorInfo()是Windows Server上的API,在PC机上不好用。所以能够尝试GetSystemInfo()来获得核数。
实际上在Windows平台上直接用任务管理器就能够查看。任务管理器上会用四个窗体来显示CPU的使用情况(Windows7 酷睿i5-3230M)
四核都显示正弦曲线算法:
參考代码:
/*********************************************************************************************/ /* 控制cpu占用率曲线(50%直线、1~100随意直线、正弦曲线)皮皮 2014-6-30 */ /********************************************************************************************/ #include <Windows.h> #include <stdio.h> #include <math.h> using namespace std; const int SAMPLE_NUM = 200; DWORD busy_time[200]; const DWORD TOTAL_TIME_SLICE = 100; //每隔100ms取下一个採样点。让cpu工作在相应振幅的时间 const DWORD HALF_TOTAL_TIME_SLICE = TOTAL_TIME_SLICE/2; //sine 曲线 void sine(){ DWORD pre_time; for(int i = 0 ; ; i = (i+1)%SAMPLE_NUM ){ pre_time = GetTickCount(); while( (GetTickCount() - pre_time) <= busy_time[i]);//cpu忙 Sleep(TOTAL_TIME_SLICE - busy_time[i]);//cpu闲 } } const double PI_L = 0.031415926535; //对sine採样 void sine_sampling(){ for(int i = 0 ; i < SAMPLE_NUM ; i++){ busy_time[i] = (DWORD)( sin( PI_L*i )*HALF_TOTAL_TIME_SLICE + HALF_TOTAL_TIME_SLICE );//busy_time值范围 (-1~1) *50+50 = (0~100) //printf("busy_time[%d] = %d\n",i,busy_time[i]); } } void main(){ sine_sampling(); //printf("CurrentProcessorNumber : %d\n",(int)GetCurrentProcessorNumber()); SYSTEM_INFO info; GetSystemInfo(&info); //调用API函数来获取计算机硬件的信息 printf("dwNumberOfProcessors : %d\n",info.dwNumberOfProcessors);//cpu核数 DWORD dwThreadId; HANDLE hThread; int coreNum = info.dwNumberOfProcessors; for(int i = 0 ; i < coreNum ; i ++){ hThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)sine,0,0,&dwThreadId); SetThreadAffinityMask(hThread,1<<i);//针对cpu核i(from 0) } WaitForSingleObject(hThread, INFINITE); //传入INFINITE表示无限等待 }
代码部分解释:
1.CreateThread
函数功能:创建线程
函数原型:
CreateThread(__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt __deref __drv_aliasesMem LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId
);
函数说明:
第一个參数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个參数表示线程栈空间大小。
传入0表示使用默认大小(1MB)。
第三个參数表示新线程所运行的线程函数地址,多个线程能够使用同一个函数地址。
第四个參数是传给线程函数的參数。
第五个參数指定额外的标志来控制线程的创建,为0表示线程创建之后马上就能够进行调度。假设为CREATE_SUSPENDED则表示线程创建后暂停执行。这样它就无法调度,直到调用ResumeThread()。
第六个參数将返回线程的ID号,传入NULL表示不须要返回该线程ID号。
函数返回值:成功返回新线程的句柄。失败返回NULL。
2.WaitForSingleObject
函数功能:等待函数 – 使线程进入等待状态。直到指定的内核对象被触发。
函数原形:
WaitForSingleObject(__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
函数说明:
第一个參数为要等待的内核对象。
第二个參数为最长等待的时间,以毫秒为单位,如传入5000就表示5秒。传入0就马上返回。传入INFINITE表示无限等待。
由于线程的句柄在线程执行时是未触发的。线程结束执行。句柄处于触发状态。所以能够用WaitForSingleObject()来等待一个线程结束执行。
函数返回值:在指定的时间内对象被触发。函数返回WAIT_OBJECT_0。超过最长等待时间对象仍未被触发返回WAIT_TIMEOUT。传入參数有错误将返回WAIT_FAILED
3.SetThreadAffinityMask()
函数功能:Sets a processor affinity mask for the specified thread.
函数原形:
WaitForSingleObject(__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
函数说明:
这个函数的參数有两个,第一个是运行任务的句柄,第二个是核的掩蔽码。
掩蔽码的使用规则是假设最低位置1,就将任务分派给编号为0的核,假设次低位置1,就将任务分派给编号为1的核。
假设掩蔽码设置为0xC。即编号为2, 3的核被置为1,线程就会在这两个核上切换。
more:
进程与指定cpu绑定:SetProcessAffinityMask(GetCurrentProcess(), dwMask);
线程与指定cpu绑定:SetThreadAffinityMask(GetCurrentThread(),dwMask);
dwMask为CPU序号的或运算值:1(0001)代表仅仅执行在CPU1,2(0010)代表仅仅执行在CPU2。3(0011)代表能够执行在CPU1和CPU2,以此类推。
ref: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686247(v=vs.85).aspx
结果截图:
附:
1.TOTAL_TIME_SLICE 的计算
假设不考虑其他程序的CPU占用情况,能够在每一个核上开一个线程。执行指定的函数,实现每一个核的CPU占用率同样。
要让CPU的占用率。呈函数 y = calc(t) (0 <= y <= 1, t为时间,单位为ms )分布。仅仅要取间隔非常短的一系列点。觉得在某个间隔内。y值近似不变。
设间隔值为GAP。显然在指定t值附近的GAP这段时间内,
CPU占用时间为:busy = GAP * calc(t)。
CPU空暇时间为:idle = GAP – busy
因此,非常easy写出以下这个通用函数:
{
double tb = 0;
while(1) {
unsigned ta = get_time();
double r = calc(tb);
if (r < 0 || r > 1) r = 1;
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
tb += GAP;
}
}
假设CPU占用率曲线不是周期性变化,就要对每一个t值都要计算一次,否则。能够仅仅计算第一个周期内的各个t值,其他周期的直接取缓存计算结果。
以CPU占用率为正弦曲线为例,显然:y = 0.5 * (1 + sin(a * t + b))
其周期T = 2 * PI / a (PI = 3.1415927),能够指定T值为60s即60000ms。则
能够确定a值为 2 * PI / T, 若在这60000ms内我们计算200次(c = 200),则GAP值为 T / c = 300ms.也就是说,仅仅要确定了周期和计算次数,其他几个參数也都确定下来。
2.CPU亲缘性(Affinity)介绍
依照默认设置。当系统将线程分配给处理器时,Windows使用软亲缘性来进行操作。这意味着假设全部其它因素同样的话,它将设法在它上次执行的那个处理器上执行线程。让线程留在单个处理器上。有助于反复使用仍然在处理器的内存快速缓存中的数据。
有一种新的计算机结构,称为NUMA(非统一内存訪问),在该结构中,计算机包括若干块插件板,每一个插 件板上有4个CPU和它自己的内存区。当CPU訪问的内存是它自己的插件板上的内存时,NUMA系统执行的性能最好。假设CPU须要訪问位于还有一个插件板上的内 存时,就会产生巨大的性能减少。在这种环境中,就须要限制来自一个进程中的线程在共享同一个插件版的CPU上执行。为了适应这种计算机结构的须要。Windows同意你设置进程和线程的亲缘性。
换句话说。你能够控制哪个CPU能够执行某些线程。
这称为硬亲缘性。
请注意。子进程能够继承进程的亲缘性。
注意:
(1)不管计算机中实际拥有多少个CPU。Windows98及曾经系统仅仅使用一个CPU,上述API不被支持。
(2)在大多数环境中,改变线程的亲缘性就会影响调度程序有效地在 各个CPU之间移植线程的能力,而这样的能力能够最有效地使用CPU时间。
3.CreateThread()函数
它是Windows提供的API接口,在C/C++语言另有一个创建线程的函数_beginthreadex(),在非常多书上(包含《Windows核心编程》)提到过尽量使用_beginthreadex()来取代使用CreateThread()。这是为什么?http://blog.csdn.net/morewindows/article/details/7421759
ref:
http://blog.csdn.net/pipisorry/article/details/36189155
http://blog.csdn.net/morewindows/article/details/7421759
http://www.cnblogs.com/foreverinside/p/3577273.html
http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991965.html
http://blog.csdn.net/u012370255/article/details/28232153
编程之美读书笔记1.1——让CPU占用率曲线听你的指挥