gpt4 book ai didi

C++ 日志记录和性能调整库

转载 作者:IT老高 更新时间:2023-10-28 23:17:37 25 4
gpt4 key购买 nike

我需要在 Windows 上对一些实时代码进行性能分析。

我不能使用任何常规分析器(Vtune、Codeanalyst),因为我不能减慢可执行文件的速度。所以我使用的是我自己的基于 QueryPerformanceCounter() 的计时类。

那么是否有任何现有的(免费)C++ 库来记录结果 - 但我需要将它们缓冲到运行结束,我没有时间在我的时候写入文件或事件日志收集数据。

我自己很容易滚动,但是如果有办法将 log4cplus/qDebug 或类似的日志记录到缓冲区并稍后转储,它将节省一些精力。

最佳答案

我也写了一个类来做这件事,因为我找不到任何东西可以做我想要的,但仍然很容易通过复制粘贴编码技术合并到现有代码中。

(编辑)请注意,正如评论中提到的,计时行为本身会改变您计时的性质。但是,我相信您对此非常了解。这个类对我发现热点和瓶颈仍然非常有用,除了给我至少一个粗略的高边估计某些代码需要多长时间运行。我在此处展示的代码从未 旨在具有生产值(value)。就这样使用它。

这是我类(class)的示例输出:

Timer Precision = 385 ns


=== Item Trials Ttl Time Avg Time Mean Time StdDev ===
DB Connect 1 0.11 s 0.11 s 0.11 s 0.0000 ps
Entry Parse 3813 0.33 s 86 us 82 us 18 us
File Load 1 50 ms 50 ms 50 ms 0.0000 ps
Option Parse 1 5 ms 5 ms 5 ms 0.0000 ps
Record Update 3812 0.53 s 140 us 127 us 49 us
============================================================================

每个“项目”都是我想要计时的一段代码。 Timer对象使用 RAII(通过 auto_ptr)来允许自动启动和停止。下面是一些示例客户端代码,演示了如何使用它:

示例代码:

int main(int ac, char* av[])
{
std::auto_ptr<dbg::Timer> section_timer(new dbg::Timer("Option Parse"));
// MAGIC: Parse the command line...

section_timer = auto_ptr<dbg::Timer>(new dbg::Timer("File Load"));
// MAGIC: Load a big file...

section_timer = auto_ptr<dbg::Timer>(new dbg::Timer("DB Connect"));
// MAGIC: Establish a DB connection...

for( /* for each item in the file*/ )
{
section_timer = auto_ptr<dbg::Timer>(new dbg::Timer("Entry Parse"));
// MAGIC: Parse the incoming item
section_timer = auto_ptr<dbg::Timer>(new dbg::Timer("Record Update"));
// MAGIC: Update the database
}

// Dump the output shown above

cout << dbg::Timer::DumpTrials() << endl;
}

这个类的实现在一个包含库中。你不需要编译任何东西。我使用 Boost.Format 进行字符串格式化,但这可以简单地替换。

这是实现(我的实际实现将它分成两个文件,core.hcore_dbg.hpp;后者是 #include 直接从前者编辑)。

实现:

namespace dbg
{
class Timer
{
public:
inline Timer(const std::string & name);
inline Timer(const Timer& rhs);
inline virtual ~Timer();
inline std::string format_now() const;
double current_elapsed() const;

Timer& operator=(const std::string& name);

static std::string DumpTrials(bool ClipboardAlso=false);
static void Reset();
static std::string format_elapsed(double d) ;

private:
typedef std::string key_type;
typedef double time_type;
typedef std::multimap<key_type, time_type> times;

static __int64 TimerFreq();

LARGE_INTEGER startTime_, stopTime_;
mutable LARGE_INTEGER intermediateTime_;
std::string label_;

static times& TimeMap();

struct extract_key : public std::unary_function<times::value_type, key_type>
{
std::string operator()(times::value_type const & r) const
{
return r.first;
}
};

struct extract_val : public std::unary_function<times::value_type, time_type>
{
time_type operator()(times::value_type const & r) const
{
return r.second;
}
};
struct match_key : public std::unary_function<times::value_type, bool>
{
match_key(key_type const & key_) : key(key_) {};
bool operator()(times::value_type const & rhs) const
{
return key == rhs.first;
}
match_key& operator=(const match_key& rhs)
{
key = rhs.key;
return * this;
}
protected:
key_type key;
};

struct accum_key : public std::binary_function<time_type, times::value_type, time_type>
{
accum_key(key_type const & key_) : key(key_), n(0) {};
time_type operator()(time_type const & v, times::value_type const & rhs) const
{
if( key == rhs.first )
{
++n;
return rhs.second + v;
}
return v;
}
private:
times::key_type key;
mutable size_t n;
};
};

inline Timer::Timer(const std::string & name)
{
label_ = name;
QueryPerformanceCounter(&startTime_);
}

inline double Timer::current_elapsed() const
{
QueryPerformanceCounter(&intermediateTime_);
__int64 clocks = intermediateTime_.QuadPart-startTime_.QuadPart;
double elapsed = (double)clocks/(double)TimerFreq();
return elapsed;
}

inline Timer::~Timer()
{
double elapsed = current_elapsed();
stopTime_.QuadPart = intermediateTime_.QuadPart;
TimeMap().insert(std::make_pair(label_,elapsed));
};

inline Timer& Timer::operator=(const std::string& name)
{
double elapsed = current_elapsed();
stopTime_.QuadPart = intermediateTime_.QuadPart;
TimeMap().insert(std::make_pair(label_,elapsed));
label_ = name;
QueryPerformanceCounter(&startTime_);
return * this;
}

inline Timer::Timer(const Timer& rhs)
{
double elapsed = current_elapsed();
stopTime_.QuadPart = intermediateTime_.QuadPart;
TimeMap().insert(std::make_pair(label_,elapsed));
label_ = rhs.label_;
QueryPerformanceCounter(&startTime_);
}

inline std::string Timer::format_now() const
{
return format_elapsed(current_elapsed());
}

inline std::string Timer::DumpTrials(bool ClipboardAlso)
{
using boost::io::group;

if( TimeMap().empty() )
{
return "No trials\r\n";
}

std::string ret = (boost::format("\r\n\r\nTimer Precision = %s\r\n\r\n")
% format_elapsed(1.0/(double)TimerFreq())).str();

// get a list of keys
typedef std::set<std::string> keyset;
keyset keys;
std::transform(TimeMap().begin(), TimeMap().end(), std::inserter(keys, keys.begin()), extract_key());

size_t maxrows = 0;

typedef std::vector<std::string> strings;
strings lines;

static const size_t t = 9;

std::string head =
(boost::format("=== %-s %-s %-s %-s %-s %-s ===")
//% (t*2)
//% (t*2)
% group(std::setw(t*2), std::setprecision(t*2), "Item" )
//% t
//% t
% group(std::setw(t), std::setprecision(t), "Trials")
//% t
//% t
% group(std::setw(t), std::setprecision(t), "Ttl Time" )
//% t
//% t
% group(std::setw(t), std::setprecision(t), "Avg Time" )
//% t
//% t
% group(std::setw(t), std::setprecision(t), "Mean Time" )
//% t
//% t
% group(std::setw(t), std::setprecision(t), "StdDev")
).str();

ret += (boost::format("\r\n%s\r\n") % head).str();
if( ClipboardAlso )
lines.push_back("Item\tTrials\tTtl Time\tAvg Time\tMean Time\tStdDev\r\n");
// dump the values for each key
for( keyset::iterator key = keys.begin(); keys.end() != key; ++key )
{
time_type ttl = 0;
ttl = std::accumulate(TimeMap().begin(), TimeMap().end(), ttl, accum_key(*key));
size_t num = std::count_if( TimeMap().begin(), TimeMap().end(), match_key(*key));
if( num > maxrows )
maxrows = num;
time_type avg = ttl / num;

// compute mean
std::vector<time_type> sortedTimes;
dibcore::stl::transform_if(TimeMap().begin(), TimeMap().end(), std::inserter(sortedTimes, sortedTimes.begin()), extract_val(), match_key(*key));
std::sort(sortedTimes.begin(), sortedTimes.end());
size_t mid = (size_t)floor((double)num/2.0);
double mean = ( num > 1 && (num % 2) != 0 ) ? (sortedTimes[mid]+sortedTimes[mid+1])/2.0 : sortedTimes[mid];

// compute variance
double sum = 0.0;
if( num > 1 )
{
for( std::vector<time_type>::iterator cur = sortedTimes.begin(); sortedTimes.end() != cur; ++cur )
sum += pow(*cur-mean,2.0);
}

// compute std dev
double stddev = num > 1 ? sqrt(sum/((double)num-1.0)) : 0.0;

ret += (boost::format(" %-s %-s %-s %-s %-s %-s\r\n")
% group(std::setw(t*2), std::setprecision(t*2) , (*key))
% group(std::setw(t), std::setprecision(t) , (boost::format("%d") %num).str() )
% group(std::setw(t), std::setprecision(t) , format_elapsed(ttl).c_str() )
% group(std::setw(t), std::setprecision(t) , format_elapsed(avg) )
% group(std::setw(t), std::setprecision(t) , format_elapsed(mean) )
% group(std::setw(t), std::setprecision(t) , format_elapsed(stddev)) ).str();

if( ClipboardAlso )
lines.push_back((
boost::format("%s\t%s\t%s\t%s\t%s\t%s\r\n")
% (*key )
% (boost::format("%d") %num)
% format_elapsed(ttl)
% format_elapsed(avg)
% format_elapsed(mean)
% format_elapsed(stddev)
).str());


}
ret += (boost::format("%s\r\n")
% std::string(head.length(),'=') ).str();

if( ClipboardAlso )
{
// dump header row of data block
lines.push_back("");
{
std::string s;
for( keyset::iterator key = keys.begin(); key != keys.end(); ++key )
{
if( key != keys.begin() )
s.append("\t");
s.append(*key);
}
s.append("\r\n");
lines.push_back(s);
}

// blow out the flat map of time values to a seperate vector of times for each key
typedef std::map<std::string, std::vector<time_type> > nodematrix;
nodematrix nodes;
for( times::iterator time = TimeMap().begin(); time != TimeMap().end(); ++time )
nodes[time->first].push_back(time->second);

// dump each data point
for( size_t row = 0; row < maxrows; ++row )
{
std::string rowDump;
for( keyset::iterator key = keys.begin(); key != keys.end(); ++key )
{
if( key != keys.begin() )
rowDump.append("\t");
if( nodes[*key].size() > row )
rowDump.append((
boost::format("%f")
% nodes[*key][row]
).str());
}
rowDump.append("\r\n");
lines.push_back(rowDump);
}

// dump to the clipboard
std::string dump;
for( strings::iterator s = lines.begin(); s != lines.end(); ++s )
{
dump.append(*s);
}

OpenClipboard(0);
EmptyClipboard();
HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, dump.length()+1);
if( hg )
{
char* buf = (char*)GlobalLock(hg);
if( buf )
{
std::copy(dump.begin(), dump.end(), buf);
buf[dump.length()] = 0;
}
GlobalUnlock(hg);
SetClipboardData(CF_TEXT, hg);
}
CloseClipboard();
}

return ret;
}

inline std::string Timer::format_elapsed(double d)
{
if( d < 0.00000001 )
{
// show in ps with 4 digits
return (boost::format("%0.4f ps") % (d * 1000000000000.0)).str();
}
if( d < 0.00001 )
{
// show in ns
return (boost::format("%0.0f ns")% (d * 1000000000.0)).str();
}
if( d < 0.001 )
{
// show in us
return (boost::format("%0.0f us") % (d * 1000000.0)).str();
}
if( d < 0.1 )
{
// show in ms
return (boost::format("%0.0f ms") % (d * 1000.0)).str();
}
if( d <= 60.0 )
{
// show in seconds
return (boost::format("%0.2f s") % d).str();
}
if( d < 3600.0 )
{
// show in min:sec
return (boost::format("%01.0f:%02.2f") % floor(d/60.0) % fmod(d,60.0)).str();
}
// show in h:min:sec
return (boost::format("%01.0f:%02.0f:%02.2f") % floor(d/3600.0) % floor(fmod(d,3600.0)/60.0) % fmod(d,60.0)).str();
}

inline void Timer::Reset()
{
TimeMap().clear();
}

inline __int64 Timer::TimerFreq()
{
static __int64 freq = 0;
static bool init = false;
if( !init )
{
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
freq = li.QuadPart;
init = true;
}
return freq;
}

inline Timer::times& Timer::TimeMap()
{
static times times_;
return times_;
}
}; // namespace dbg

关于C++ 日志记录和性能调整库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4727006/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com