windows下一个统计模块运行时间、模块内存消耗的cpp类

需求

win下、vs开发环境、cpp开发的类库,用于统计软件系统各模块的运行时间、内存消耗,输出为csv文件,用excel打开后,按照时间排序就可以分析各模块的时间效率、空间效率问题。

使用方式

接口只有4个宏:
初始化宏GlobalTimeLogStart()和资源释放宏GlobalTimeLogEnd()在整个进程运行过程中只调用一次。
csv文件一般是在进程退出时才会生成。
csv文件的生成路径可以自己修改一下,在WriteToCsvTimeLogFile函数中。

要检测的模块处于两个宏之间即可,
例如:进入函数的地方写EventTimeStart(“program name 1”)
退出函数的地方写EventTimeEnd(“program name 1”)
模块名字符串时任意的,尽量只用英文数字小数点,不要加,号(csv是,分隔的)。模块名也可以动态生成,只要EventTimeStart、EventTimeEnd用的是同一个名字即可。

#define TimeLogAction 0 时所有宏失效(需要重新编译)
#define TimeLogAction 1 时所有宏有效(需要重新编译)

输出的csv文件数据类似如下:
模块名,起始时间点,结束时间,持续时间,时间占比,起始内存,结束内存,增长内存
GetPlugin.optionplugin, 0.176849, 0.210219, 0.033370, 0.144139, 28.753906, 28.855469, 0.101563
GetPlugin.plugin, 0.223333, 0.228646, 0.005313, 0.022951, 28.984375, 30.773438, 1.789063
GetPlugin.baseoptionplugin, 0.210760, 0.222796, 0.012036, 0.051991, 28.855469, 28.984375, 0.128906
GetPlugin.basicplugin, 0.172980, 0.174732, 0.001752, 0.007566, 28.320313, 28.738281, 0.417969

代码

头文件timelog.h

//导出宏定义
#ifdef TIME_LOG_LIB
# define TIME_LOG_EXPORT __declspec(dllexport)
#else
# define TIME_LOG_EXPORT __declspec(dllimport)
#endif

//避免重复包含宏定义
#ifndef TIME_LOG_H
#define TIME_LOG_H

#include <vector>
#include <map>
#include <set>
#include <string>
#include <Windows.h> //LARGE_INTEGER相关定义

#define TimeLogAction 0

#if TimeLogAction
#define GlobalTimeLogStart() TimeLog::TimeLogStart()
#define EventTimeStart(x) TimeLog::GetTimeLogInstance()->EventStart(x,__FILE__,__LINE__)
#define EventTimeEnd(x) TimeLog::GetTimeLogInstance()->EventEnd(x)
#define WriteCsvTimeLog() TimeLog::GetTimeLogInstance()->WriteToCsvTimeLogFile()
#define GlobalTimeLogEnd() WriteCsvTimeLog()
#else
#define GlobalTimeLogStart()
#define EventTimeStart(x)
#define EventTimeEnd(x)
#define WriteCsvTimeLog()
#define GlobalTimeLogEnd() WriteCsvTimeLog()
#endif

class TIME_LOG_EXPORT TimeMemCounter
{
public:
    static size_t GetMemB();//当前内存占用量,以byte为单位
    //static size_t GetMem(/*int pid = 0*/);//当前内存占用量,以K byte为单位
    static double GetMemK(){
        return GetMemB() / 1024.0;//当前内存占用量,以K byte为单位
    }
    static double GetMemM(){
        return GetMemB() / (1024.0 * 1024.0);//当前内存占用量,以M byte为单位
    }
    static double GetMemG(){
        return GetMemB() / (1024.0 * 1024.0 * 1024.0);//当前内存占用量,以G byte为单位
    }
public:
    void SetStart()								//起始计时点
    {
        QueryPerformanceCounter(&start_time_);
    }
    void SetEnd()								//终止计时点
    {
        QueryPerformanceCounter(&total_time_);
        total_time_.QuadPart -= start_time_.QuadPart;		//两个计时点之间的时间差
    }
    double GetTimeBySecond()const						//总时间,以秒为单位
    {
        return static_cast<double>(total_time_.QuadPart) / frequency_.QuadPart;
    }
    TimeMemCounter(){
        QueryPerformanceFrequency(&frequency_);			//系统时钟频率
    }
    ~TimeMemCounter(){}
private:
    LARGE_INTEGER frequency_;
    LARGE_INTEGER start_time_;
    LARGE_INTEGER total_time_;
};

class StartEndTime{
public:
    LARGE_INTEGER frequency_;			//系统时钟频率
    LARGE_INTEGER start_time_;          //单个事件的起始时间
    LARGE_INTEGER end_time_;            //单个事件的结束时间
    size_t start_mem_;                  //单个事件的起始内存
    size_t end_mem_;                    //单个事件的结束内存
public:
    StartEndTime()
    {
        QueryPerformanceFrequency(&frequency_);
        QueryPerformanceCounter(&start_time_);
        end_time_ = start_time_;//默认值是前后相等
        //
        start_mem_ = static_cast<size_t>(TimeMemCounter::GetMemK());
        end_mem_ = start_mem_;
    }
    ~StartEndTime(){}
    bool IsSameStartEnd()
    {
        return end_time_.QuadPart == start_time_.QuadPart;
    }
    void SetEnd()
    {
        QueryPerformanceCounter(&end_time_);
        end_mem_ = static_cast<size_t>(TimeMemCounter::GetMemK());
    }
    double GetTimeBySecond()const						//总时间,以秒为单位
    {
        return static_cast<double>(end_time_.QuadPart - start_time_.QuadPart) / frequency_.QuadPart;
    }
    double GetMemByM()const						        //内存总消耗,以M为单位
    {
        return (end_mem_ * 1.0 - start_mem_ * 1.0) / (1024);
    }
};
typedef std::vector<StartEndTime> StartEndTimeVector;

class EventStartEndTime{
public:
    std::string event_name_;//事件名称,例如函数名
    //std::string start_file_line_;//代码所在文件名和行号
    //std::string end_file_line_;//代码所在文件名和行号
    StartEndTimeVector time_vec_;//事件时间队列,串行、嵌套。并行时间无法统计,需要用不同名称。
public:
    void StartOne()
    {
        time_vec_.push_back(StartEndTime());
    }
    void EndOne()
    {
        //从后往前找第一个前后相等的时间。
        //支持同名嵌套事件。支持任意个同名串行事件。并行事件:不支持同名并行,但支持不同名并行。
        for (StartEndTimeVector::reverse_iterator i = time_vec_.rbegin();
            i != time_vec_.rend(); ++i)
        {
            if (i->IsSameStartEnd())
            {
                i->SetEnd();
                break;
            }
        }
    }
};

class EventFileLine
{
public:
    EventFileLine(const std::string &event_name, const std::string &file_name, const int line_temp)
        :event_name_(event_name), file_name_(file_name), line_(line_temp){}
    EventFileLine(const EventFileLine &t)
        :event_name_(t.event_name_), file_name_(t.file_name_), line_(t.line_){}
    ~EventFileLine(){}
    EventFileLine &operator=(const EventFileLine &t)
    {
        if (this == &t)return *this;
        event_name_ = t.event_name_;
        file_name_ = t.file_name_;
        line_ = t.line_;
        return *this;
    }
    bool operator<(const EventFileLine &t)const
    {
        if (line_ != t.line_)
        {
            return line_ < t.line_;
        }
        if (event_name_ != t.event_name_)
        {
            return event_name_.compare(t.event_name_) < 0;
        }
        return file_name_.compare(t.file_name_) < 0;
    }
    bool operator==(const EventFileLine &t)const
    {
        if (line_ != t.line_)
        {
            return false;
        }
        if (event_name_ != t.event_name_)
        {
            return false;
        }
        return file_name_ == t.file_name_;
    }
    std::string ToString()const{
        std::string s = event_name_ + "," + file_name_ + ":" + std::to_string(line_);
        return s;
    }
public:
    EventFileLine(){}
public://建议同一事件只出现在一个地方。但本类支持不同地方的事件重名。
    std::string event_name_;//事件名称,例如函数名
    std::string file_name_;//事件所在的文件名
    int line_;//事件所在的行号
};

typedef std::map<std::string, EventStartEndTime*> EventStartEndTimeMap;
typedef std::multimap<double, std::string> EventStartEndTimeSortMap;
typedef std::set<EventFileLine> EventFileLineSet;
typedef std::vector<EventFileLine> EventFileLineVector;

//导出警告问题
template class __declspec(dllexport) std::basic_string<char>;
template class __declspec(dllexport) std::map < std::basic_string<char>, EventStartEndTime* > ;
template class __declspec(dllexport) std::multimap < double, std::basic_string<char> > ;
template class __declspec(dllexport) std::set < EventFileLine > ;
template class __declspec(dllexport) std::vector < EventFileLine > ;

/** @brief 时间日志类 */
class TIME_LOG_EXPORT TimeLog
{
private:
    TimeLog();
    ~TimeLog();
private:
    LARGE_INTEGER frequency_;           //系统时钟频率
    LARGE_INTEGER start_time_;          //整个程序统计时间的起始时间
    LARGE_INTEGER end_time_;            //整个程序统计时间的结束时间
    double GetTimeBySecond()const						//总时间,以秒为单位
    {
        return static_cast<double>(end_time_.QuadPart - start_time_.QuadPart) / frequency_.QuadPart;
    }
private:
    EventStartEndTimeMap event_time_map_; //各种事件的时间队列
    EventStartEndTimeSortMap event_time_sort_map_; //按持续时间排序
    //EventFileLineSet event_file_line_set_; //各事件所在的代码文件名行号
    EventFileLineVector event_file_line_vec_; //各事件所在的代码文件名行号
private:
    std::string csv_file_name_;
public:
    void EventStart(const std::string &event_name, const std::string &file_name, const int line_temp);
    void EventEnd(const std::string &event_name);
public:
    void WriteToCsvTimeLogFile(/*const std::string &file_name*/);
public:
    static void TimeLogStart(); //整个程序统计时间的起始
    static void TimeLogEnd();   //整个程序统计时间的结束
public:
    static TimeLog *GetTimeLogInstance();//获得单例接口
private:
    static TimeLog *gTimeLog;//单例
public:
    class GlobalRelease
    {
    public:
        ~GlobalRelease()
        {
            if (TimeLog::gTimeLog)
            {
                delete TimeLog::gTimeLog;
                TimeLog::gTimeLog = 0;
            }
        }
    };
    static GlobalRelease gGlobalRelease;//负责单例TimeLog::gTimeLog的内存释放
};
#endif // TIME_LOG_H

实现文件timelog.cpp

#include "common/log/timelog.h"
#include "psapi.h"
#include <process.h>
#include <fstream>
#include <time.h>

TimeLog *TimeLog::gTimeLog = NULL;
TimeLog::GlobalRelease TimeLog::gGlobalRelease;

TimeLog::TimeLog()
{
    QueryPerformanceFrequency(&frequency_);			//系统时钟频率
    QueryPerformanceCounter(&start_time_);
    end_time_ = start_time_;//默认值是前后相等
}

TimeLog::~TimeLog()
{
    for (EventStartEndTimeMap::iterator i = event_time_map_.begin();
        i != event_time_map_.end(); ++i)
    {
        delete i->second;
    }
    event_time_map_.clear();
}

TimeLog* TimeLog::GetTimeLogInstance()
{
    if (gTimeLog == NULL)
    {
        gTimeLog = new TimeLog;
    }
    return gTimeLog;
}

void TimeLog::TimeLogStart()
{
    GetTimeLogInstance();
}

void TimeLog::TimeLogEnd()
{
    QueryPerformanceCounter(&(GetTimeLogInstance()->end_time_));
}

void TimeLog::EventStart(const std::string &event_name, const std::string &file_name, const int line_temp)
{
    EventStartEndTime *p = NULL;
    EventStartEndTimeMap::iterator i = event_time_map_.find(event_name);
    if (i != event_time_map_.end())
    {
        p = i->second;
    }
    else
    {
        p = new EventStartEndTime;
        p->event_name_ = event_name;
        event_time_map_.insert(EventStartEndTimeMap::value_type(event_name, p));
    }
    p->StartOne();
    //
    //event_file_line_set_.insert(EventFileLine(event_name, file_name, line_temp));
    event_file_line_vec_.push_back(EventFileLine(event_name, file_name, line_temp));
}

void TimeLog::EventEnd(const std::string &event_name)
{
    EventStartEndTime *p = NULL;
    EventStartEndTimeMap::iterator i = event_time_map_.find(event_name);
    if (i != event_time_map_.end())
    {
        p = i->second;
    }
    else
    {
        p = new EventStartEndTime;
        p->event_name_ = event_name;
        event_time_map_.insert(EventStartEndTimeMap::value_type(event_name, p));
    }
    p->EndOne();
}

void TimeLog::WriteToCsvTimeLogFile(/*const std::string &file_name*/)
{
    TimeLogEnd();
    //
    time_t t = time(0);
    char time_tmp[512];
    strftime(time_tmp, sizeof(time_tmp), "%Y%m%d%H%M%S", localtime(&t));
    csv_file_name_ = time_tmp;
    //
    std::string event_file_line_file_name = std::string("../../../log/") + csv_file_name_ + "fileline.csv";
    csv_file_name_ = std::string("../../../log/") + csv_file_name_ + "timelog.csv";
    std::fstream file(csv_file_name_.c_str(), std::ios::out);
    std::fstream file2(event_file_line_file_name.c_str(), std::ios::out);
    if (file2.is_open())
    {
        file2 << "模块名,代码文件名:行号" << std::endl;
        for each (const EventFileLine &efl in event_file_line_vec_)
        {
            file2 << efl.ToString() << std::endl;
        }
        file2.close();
    }
    if (file.is_open())
    {
        //time_t t = time(0);
        char tmp[512];//可能带有文件名等信息
        //strftime(tmp, sizeof(tmp), "%Y.%m.%d.%H:%M:%S : ", localtime(&t));
        //file << tmp << "time log write start." << std::endl;//取消总时间统计,不方便显示
        //
        double total_time = GetTimeBySecond();
        //sprintf_s(tmp, sizeof(tmp), "total second of program , %lf", total_time);
        //file << tmp << std::endl;
        file << "模块名,起始时间,结束时间,持续时间,时间占比,起始内存,结束内存,增长内存" << std::endl;
        //
        for (EventStartEndTimeMap::iterator i = event_time_map_.begin();
            i != event_time_map_.end(); ++i)
        {
            EventStartEndTime *p = i->second;
            //
            double event_time_total = 0;
            double percent_total = 0;
            for (StartEndTimeVector::iterator j = p->time_vec_.begin();
                j != p->time_vec_.end(); ++j)
            {
                //相对于整个程序的起始时间的本模块的起始时间、本模块整体时间、时间占比
                double start_time = static_cast<double>(j->start_time_.QuadPart - start_time_.QuadPart) / frequency_.QuadPart;
                double end_time = static_cast<double>(j->end_time_.QuadPart - start_time_.QuadPart) / frequency_.QuadPart;
                double event_time = j->GetTimeBySecond();
                double percent_temp = event_time / total_time * 100;
                //20170605 新增输出:起始内存,结束内存,增长内存
                double start_mem = static_cast<double>(j->start_mem_) / (1024);
                double end_mem = static_cast<double>(j->end_mem_) / (1024);
                double event_mem = j->GetMemByM();
                //
                sprintf_s(tmp, sizeof(tmp), "%s, %lf, %lf, %lf, %lf, %lf, %lf, %lf", //20170627 取消内存单位,因为影响excel排序
                    p->event_name_.c_str(), start_time, end_time, event_time, percent_temp, start_mem, end_mem, event_mem);
                file << tmp << std::endl;
                //
                event_time_total += event_time;
                percent_total += percent_temp;
            }
            //sprintf_s(tmp, sizeof(tmp), "%s, total, %lf, %lf", p->event_name_.c_str(), event_time_total, percent_total);
            //file << tmp << std::endl;
            event_time_sort_map_.insert(EventStartEndTimeSortMap::value_type(event_time_total, p->event_name_));
            //
            delete p;
        }
        event_time_map_.clear();

        //取消模块的累计时间统计,不方便显示
        //for (EventStartEndTimeSortMap::const_iterator i = event_time_sort_map_.begin();
        //    i != event_time_sort_map_.end(); ++i)
        //{
        //    sprintf_s(tmp, sizeof(tmp), "%s, %lf, %lf", i->second.c_str(), i->first, i->first / total_time * 100);
        //    file << tmp << std::endl;
        //}
        //
        //strftime(tmp, sizeof(tmp), "%Y.%m.%d.%H:%M:%S : ", localtime(&t));
        //file << tmp << "time log write end." << std::endl;
        //
        file.close();
    }
}

//获得当前进程的内存占用量
size_t TimeMemCounter::GetMemB()
{
    static HANDLE hProcess = NULL;
    static bool ini_flag = false;
    if (ini_flag == false)
    {
        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, _getpid());
        ini_flag = true;
    }
    if (NULL == hProcess)
    {
        return 0;
    }
    PROCESS_MEMORY_COUNTERS pmc;
    BOOL bGet = GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc));
    if (bGet)
    {
        return pmc.PagefileUsage;//内存占用量,单位byte
    }
    else
    {
        return 0;
    }
}

windows内存结构体PROCESS_MEMORY_COUNTERS解释

//typedef struct _PROCESS_MEMORY_COUNTERS {
// DWORD cb;
// DWORD PageFaultCount; 缺页中断次数
// SIZE_T PeakWorkingSetSize; 使用内存高峰
// SIZE_T WorkingSetSize; 当前使用的内存
// SIZE_T QuotaPeakPagedPoolUsage; 使用页面缓存池高峰
// SIZE_T QuotaPagedPoolUsage; 使用页面缓存池
// SIZE_T QuotaPeakNonPagedPoolUsage; 使用非分页缓存池高峰
// SIZE_T QuotaNonPagedPoolUsage; 使用非分页缓存池
// SIZE_T PagefileUsage; 使用分页文件
// SIZE_T PeakPagefileUsage; 使用分页文件高峰
//} PROCESS_MEMORY_COUNTERS, *PPROCESS_MEMORY_COUNTERS;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值