Windows via C/C++:线程的执行时间(1)
来源:互联网 发布:java和hadoop 编辑:程序博客网 时间:2024/06/05 18:15
计算线程执行某项任务消耗的时间时,许多开发人员会调用GetTickCount/GetTickCount64编写如下的代码:
// Get the current time (start time)ULONGLONG qwStartTime = GetTickCount64();// Perform complex algorithm here// Subtract start time from current time to get durationULONGLONG dwElapsedTime = GetTickCount64() - qwStartTime;
这段代码假设当前线程不会被中断。然而在Windows这样的基于优先级的操作系统中,开发人员无法得知线程被调度的准确时间。当线程在执行任务中被中断时,使用上面的方法根本无法获得线程所消耗的时间。我们需要一个可以返回线程消耗的CPU时间(既被调度时间)的函数,幸运的是,在Vista之前的操作系统已经提供了GetThreadTimes做到这一点:
BOOL GetThreadTimes(HANDLE hThread, PFILETIME pftCreationTime,PFILETIME pftExitTime,PFILETIME pftKernelTime,PFILETIME pftUserTime);
GetThreadTime函数会将线程相关的时间信息写入为其传递的PFILETIME参数中,各个参数返回值的含义如下表所示:
使用GetThreadTimes可以计算线程所消费的CPU时间,比如下面的代码:
__int64 FileTimeToQuadWord(PFILETIME pft) { return (Int64ShllMod32(ptf->dwHighDateTime, 32) | ptf->dwLowDateTime);}void PerformLongOperation() { FILETIME ftKernelTimeStart, ftKernelTimeEnd; FILETIME ftUserTiimeStart, ftUserTimeEnd; FILETIME ftDummy; __int64 qwKernelTimeElapsed, qwUserTimeElapsed, qwTotalTimeElapsed; // Get starting times GetThreadTimes(GetCurrentThread(), &ftDummy, &ftDummy, &ftKernelTimeStart, &ftUserTimeStart); // Perform complex algorithm here ... // Get the ending times GetThreadTimes(GetCurrentThread(), &ftDummy, &ftDummy, &ftKernelTimeEnd, &ftUserTimeEnd); // Get the elapsed kernel and user times by converting the start // and the times from FILETIMEs to quad words, and then subtract // the start times from the end times. qwKernelTimeElapsed = FileTimeToQuadWord(&ftKernelTimeEnd) - FileTimeToQuadWord(&ftKernelTimeStart); qwUserTimeElapsed = FileTimeToQuadWord(&ftUserTimeEnd) - FileTimeToQuadWord(&ftUserTimeStart); // Get total time duration by adding the kernel and user times. qwTotalTimeElapsed = qwKernelTimeElapsed + qwUserTimeElapsed; }
函数GetProcessTimes用来返回进程中所有线程累计的时间信息:
BOOL GetProcessTimes( HANDLE hProcess, PFILETIME pftCreationTime, PFILETIME pftExitTime, PFILETIME pftKernelTime, PFILETIME pftUserTime);
GetProcessTimes的返回值是进程中所有线程(包括已终止线程)的累计信息,比如,pftKernelTime参数返回进程中所有线程在内核模式下执行的所有时间之和。
上面讨论的方法适合Vista及之前的系统,但在Vista中,查询线程CPU时间的方法有些变化。Vista不再依赖间隔约为10~15毫秒的系统内部定时器,而是使用处理器的时间戳计数器(Time Stamp Counter,TSC),该计数器使用64位的值记录系统启动以来的CPU周期数。在目前广泛使用的GHz级处理器上,这种方法显然要比毫秒值精确的多。
当线程被停止调度时,系统会计算当前TSC和线程开始调度时TSC的差值,并将该值累加到线程消耗的CPU周期数中。QueryThreadCycleTime函数和QueryProcessCycleTime函数返回指定线程消耗的CPU周期数或指定进程中所有线程的消耗的CPU周期数之和。此外,可以使用ReadTimeStampCounter返回自上次重置以来系统的TSC值,ReadTimeStampCounter是定义在WinNT.h中的宏,指向C++编译器提供内置函数__rdtsc。
对于精度要求较高的分析,GetThreadTimes可能无法胜任,为此Windows提供了以下两个高精度性能分析函数:
BOOL QueryPerformanceFrequency(LARGE_INTEGER* pliFrequency);
BOOL QueryPerformanceCounter(LARGE_INTEGER* pliCount);
函数QueryPerformanceFrequency返回当前硬件平台的高精度性能计数器(High-resolution Performance Counter,HRPC)的频率,注意该值并不是CPU的主频。假如当前硬件平台不支持HRPC,则函数返回0,否则返回非0值。QueryPerformanceCounter返回当前HRPC的值,若当前硬件平台不支持HRPC,函数返回0,否则返回非0值。要注意这两个函数假设调用者线程不会被抢占,不过大多数高精度的分析是在小段代码块内完成的,因此这一点不用担心。下面是我使用这些函数包装的一个C++类,可以很方便的用来进行时间性能分析:
class CStopwatch {public: CStopwatch() { QueryPerformanceFrequency(&m_liPerfFreq); Start(); }; void Start() { QueryPerformanceCounter(&m_liPerfStart); } __int64 Now() const { // 返回自Start调用以来的毫秒数 LARGE_INTEGER liPerfNow; QueryPerformanceCounter(&liPerfNow); return (liPerfNow.QuadPart - liPerfStart.QuadPart)*1000/m_liPerfFreq.QuadPart; } __int64 NowInMicro() const { // 返回自Start调用以来的微秒数 LARGE_INTEGER liPerfNow; QueryPerformanceCounter(&liPerfNow); return (liPerfNow.QuadPart - liPerfStart.QuadPart)*1000000/m_liPerfFreq.QuadPart; }private: LARGE_INTEGER m_liPerfFreq; // HSPC的频率 LARGE_INTEGER m_liPerfStart; // HSPC的初始值};
下面是一个使用CStopwatch类的例子:
CStopwatch stopwatch;// 在此处执行待测试的代码...// 获得代码执行时间__int64 qwElapsedTime = stopwatch.Now();
除了测试代码执行时间,还可以使用上面的函数估算当前计算机系统CPU的主频,代码如下:
DWORD GetCpuFrequencyInMHz() { // change the priority to ensure the thread will have more chances // to be scheduled when Sleep() ends int currentPriority = GetThreadPriority(GetCurrentThread()); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); // Keep track of the elapsed time with the other timer __int64 elapsedTime = 0; // Create a stopwatch timer which defaults to the current time __int64 perfCountStart = stopwatch.NowInMicro(); // get the current number of cycles unsigned __int64 cyclesOnStart = ReadTimeStampCounter(); // wait for about 1 second Sleep(1000); // get the number of cycles after about 1 second unsigned __in64 numberOfCycles = ReadTimeStampCounter() - cyclesOnStart; // Get how much time has elapsed with greater precision elpasedTime = stopwatch.NowInMicro() - perfCountStart; // Restore the thread priority SetThreadPriority(GetCurrentThread(), currentPriority); // Compute the frequency in MHz return (DWORD)(numberOfCycles/elaspedTime);}
上面代码的含义比较清晰,只是要注意用这种方法得到的只是CPU频率的估计值,因为Sleep函数的调用效果大多数情况下不可能精确到指定的1秒,且上述方法假设CPU不具备自动调频的能力。
- Windows via C/C++:线程的执行时间(1)
- Windows via C/C++:线程的执行时间
- Windows Via C/C++:线程的挂起和恢复
- Windows Via C/C++:线程的睡眠和切换
- 《Windows via C/C++》学习笔记(二线程)
- 《Windows via C/C++》学习笔记(三)线程
- Windows Via C/C++:线程概述
- Windows Via C/C++:线程实现细节
- 《windows via C++》之windows线程同步
- 《windows via C++》之windows线程同步
- 《windows via C++》之windows线程同步
- 《Windows via C/C++》学习笔记(四)用户模式的“线程同步”
- 《Windows via C/C++》学习笔记(五) 内核对象的“线程同步”
- 《Windows via C/C++》学习笔记 —— Windows 线程
- 《Windows via C/C++》学习笔记 —— Windows 线程池 (转)
- 《Windows via C/C++》学习笔记 (八) Windows 线程池 纤程
- Windows Via C/C++:用户模式下的线程同步——轻量级读写锁(Slim Reader-Writer Locks)
- Windows Via C/C++:线程入口点函数
- 冒泡排序算法与选择排序算法分析(C描述)
- 前端简单处理input输入跨域攻击
- sql优化中遇到的问题
- Java中ArrayList类的用法
- Java synchronized详解
- Windows via C/C++:线程的执行时间(1)
- ViewPager 两边显示部分其他页
- Unity中文件流和WWW的方式加载图片
- liferay6.2通过事件机制实现不同portlet之间数据传递
- 2017年伊始,你需要尝试的25个Android第三方库
- 合并Shader系列 | 如何合并渲染状态
- linux中lost+found目录的作用
- Git学习文档之一 学习文档-发布
- 如何使文字垂直居中