在进程的性能数据采集过程中,经常用到的一个性能指标就是进程的cpu占用率,下面给出它的计算方法及示例代码。
1、CPU占用率的定义
CPU占用率:指进程在一个时间段内消耗的CPU时间与该时间段长度的比值。
2、CPU占用率计算方法
根据上述定义,可以得到进程CPU占用率计算公式如下:
进程消耗的CPU时间 = 进程消耗的内核态时间 + 进程消耗的用户态时间,即 costTime = kernelTime + UserTime
进程的CPU占用率 = 进程消耗的CPU时间 / 刷新周期(总耗时)
void McuUsageThread::run(){
THREADENTRY32 te32 = {0};
FILETIME ftTimeCreation = {0};
FILETIME ftTimeExit = {0};
FILETIME ftTimeKernel = {0};
FILETIME ftTimeUser = {0};
FILETIME ftLastTimeKernel = {0};
FILETIME ftLastTimeUser = {0};
HANDLE hSnap = NULL;
LARGE_INTEGER liPerfFreq = {0};
LARGE_INTEGER liPerfCountLast = {0};
LARGE_INTEGER liPerfCountCur = {0};
LONGLONG llPerfDelta = 0;
LONGLONG llUserCur = 0;
LONGLONG llKernelCur = 0;
LONGLONG llUserLast = 0;
LONGLONG llKernelLast = 0;
LONGLONG llKernelDelta = 0;
LONGLONG llUserDelta = 0;
if (!QueryPerformanceFrequency((LARGE_INTEGER*)&liPerfFreq))
{
//类型:Win32API
//原型:BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
//作用:返回硬件支持的高精度计数器的频率。
//返回值:非零,硬件支持高精度计数器;零,硬件不支持,读取失败
//liPerfFreq 为获取的时钟频率
return;
}
CeSetThreadPriority(GetCurrentThread(), 100);
//设置线程的优先级,GetCurrentThread()线程的句柄,第二个参数是线程的优先级
while (1)
{
Sleep(m_u32SampleTime_ms);
llKernelCur = 0;
llUserCur = 0;
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
//建立一个快照,获取进程列表
//参数1,用来指定快照中需要返回的对象,
//参数2,一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0
//具体看下一篇博文,
if (hSnap != INVALID_HANDLE_VALUE)
{
te32.dwSize = sizeof(te32);
if (Thread32First(hSnap, &te32)) //获取快照中第一个线程信息
{
do
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
if (hThread)
{
GetThreadTimes(hThread, &ftTimeCreation,
&ftTimeExit, &ftTimeKernel, &ftTimeUser);
//获取线程时间
//参数1,线程句柄;参数2:创建时间;参数3:退出时间;参数4:内核时间;参数5:用户时间
llKernelCur += *((LONGLONG*)(&ftTimeKernel)) - *((LONGLONG*)(&ftLastTimeKernel));
llUserCur += *((LONGLONG*)(&ftTimeUser)) - *((LONGLONG*)(&ftLastTimeUser));
CloseHandle(hThread);
}
} while (Thread32Next(hSnap, &te32));//获取快照中下一个线程信息
}
CloseToolhelp32Snapshot(hSnap);//关闭快照
}
QueryPerformanceCounter(&liPerfCountCur);
llPerfDelta = liPerfCountCur.QuadPart - liPerfCountLast.QuadPart; //计数
llPerfDelta = 10000000 * llPerfDelta/liPerfFreq.QuadPart; //转化为时间,10000000是飞秒单位换算出来的
//总耗时=时钟数/频率 =时钟数*(一个时钟数的时间1/频率);
liPerfCountLast = liPerfCountCur;
llKernelDelta = llKernelCur - llKernelLast; //内核消耗的时间
llUserDelta = llUserCur - llUserLast; //用户消耗的时间
llKernelLast = llKernelCur;
llUserLast = llUserCur;
INT32 dCPUKernel;
dCPUKernel = MATH_MAX(0, MATH_MIN(100.0, 100.0 * llKernelDelta/llPerfDelta));
//内核消耗的时间的百分比
INT32 dCPUsage;
dCPUsage = MATH_MAX(dCPUKernel, MATH_MIN(100.0, 100.0 * (llUserDelta + llKernelDelta)/llPerfDelta));
//用户消耗加内核消耗时间百分比
MEMORYSTATUS memStatus;
memStatus.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus(&memStatus);//获取内存信息
//MEMORYSTATUS结构体分析
/*
typedef struct _MEMORYSTATUS {
DWORD dwLength; //本结构体的长度
DWORD dwMemoryLoad; //已用内存的百分比
DWORD dwTotalPhys; //物理内存总量
DWORD dwAvailPhys; //可用物理内存
DWORD dwTotalPageFile; //交换文件总的大小
DWORD dwAvailPageFile; //交换文件中空闲部分的大小
DWORD dwTotalVirtual; //用户可用的地址空间
DWORD dwAvailVirtual; //当前空闲的地址空间
} MEMORYSTATUS, *LPMEMORYSTATUS;
*/
PRINTF_MSG("@@@@@@@@@@@======================== dCPUsage is %d , dCPUKernel is %d ", dCPUsage, dCPUKernel);
PRINTF_MSG("@@@@@@@@@@@======================== UsageMem is %s, TotalMem is %s ", FormatMemory_toString(memStatus.dwTotalPhys - memStatus.dwAvailPhys).toUtf8().data(), FormatMemory_toString(memStatus.dwTotalPhys).toUtf8().data());
}
}
QString McuUsageThread::FormatMemory_toString(quint32 f_u32BytesNum) {//打印需要的
quint32 t_u32bt = f_u32BytesNum & 0x2FF;
quint32 t_u32Kb = (f_u32BytesNum>>10) & 0x2FF;
quint32 t_u32Mb = (f_u32BytesNum>>20);
return QString::number(t_u32Mb) + "Mb " + QString::number(t_u32Kb) + "Kb " + QString::number(t_u32bt) + "byte";
}
/******************************************************************************************************/
class McuUsageThread : public QThread //创建线程用于获取打印cpu内存的占用率
{
public:
quint32 m_u32SampleTime_ms;
McuUsageThread(){
m_u32SampleTime_ms = 3500;
}
QString FormatMemory_toString(quint32 f_u32BytesNum);
public:
void run();
};
/******************************************************************************************************/
总结:整个代码的思路就是:
1.获取系统时钟频率
2.设置单前线程的优先级(觉得可以不用,没有验证不设置的正确性)
3.创建一个线程快照遍历,
4.根据快照获取线程的相关时间,并计算所有线程的耗时。
5.获取计时器的值,(高精度硬件支持的计时器)。
6.通过获取到的计时器的值和系统时钟频率计算出总耗时.
7.分别计算出内核耗时百分比,和用户耗时百分比。
内存的占用计算比较简单就不多说了,看代码都能懂。