1 /*---------------------------------------------------------------------------*\
5 * Copyright (C) 2000-2006 by the OpenSG Forum *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
37 \*---------------------------------------------------------------------------*/
39 #include "OSGConfig.h"
43 #include "OSGPerfMonitor.h"
44 #include <boost/format.hpp>
45 #include "OSGBaseInitFunctions.h"
46 #include "OSGSingletonHolder.ins"
47 #include "OSGBaseFunctions.h"
48 #include "OSGThread.h"
53 OSG_SINGLETON_INST(PerfMonitorBase
, addPostFactoryExitFunction
)
55 void NestedSampleInfo::updateFrame()
57 typedef subsample_map_t::iterator iter_type
;
59 // Update the children
60 for (iter_type i
= mSubSamples
.begin(); i
!= mSubSamples
.end(); ++i
)
62 (*i
).second
->updateFrame();
65 // Compute the limit on number of samples to hold
66 UInt32 sample_limit
= NestedPerfTracker::max_samples
;
68 // Compute my sample value and average
69 mSamples
.push_front(mTimeSummer
);
71 while(mSamples
.size() > sample_limit
)
76 // Compute max and average
79 for(UInt32 i
=0;i
<mSamples
.size();++i
)
81 Real32
val(mSamples
[i
]);
88 if (mSamples
.size() > 0)
90 mAverage
= total
/ static_cast<Real32
>(mSamples
.size());
93 // Calculate the percentages of averages for each subsample
94 for (iter_type i
= mSubSamples
.begin(); i
!= mSubSamples
.end(); ++i
)
96 NestedSampleInfoPtr
child((*i
).second
);
97 child
->calcPercentage(mAverage
);
101 void NestedSampleInfo::calcPercentage(const Real32 parentAverage
)
103 if(parentAverage
== 0.0f
)
109 mPercentage
= mAverage
/parentAverage
;
112 // Add the percentage to the list
113 mPctSamples
.push_front(mPercentage
);
114 while(mPctSamples
.size() > NestedPerfTracker::max_samples
)
116 mPctSamples
.pop_back();
120 std::string
NestedSampleInfo::outString(UInt32 indent
, bool detailed
)
122 std::ostringstream ostring
;
123 //std::string indent_str(indent*2, ' ');
124 std::string
indent_str(" ");
125 ostring
<< indent_str
<< mName
<< "[" << mPercentage
<< "] [" << mAverage
127 // boost::format("[%.4f] [%.4f]\n")%mPercentage%mAverage;
130 ostring
<< " samples:[";
131 typedef sample_list_t::iterator iter_type
;
132 for (iter_type i
= mSamples
.begin(); i
!= mSamples
.end(); ++i
)
134 ostring
<< (*i
) << ", ";
139 std::string ret_string
= ostring
.str();
140 typedef subsample_map_t::iterator iter_type
;
141 for (iter_type i
= mSubSamples
.begin(); i
!= mSubSamples
.end(); ++i
)
143 ret_string
+= (*i
).second
->outString(indent
+ 1, detailed
);
148 void NestedPerfTracker::updateFrame()
150 // Update the entire tree
151 mPerfRoot
->updateFrame();
153 // Compute average for root
154 mPerfRoot
->mAverage
= 0.0;
155 typedef NestedSampleInfo::subsample_map_t::iterator iter_type
;
156 for(iter_type i
= mPerfRoot
->mSubSamples
.begin();
157 i
!= mPerfRoot
->mSubSamples
.end(); ++i
)
159 mPerfRoot
->mAverage
+= (*i
).second
->mAverage
;
162 // Compute overall framerate
163 OSG::TimeStamp
cur_time(OSG::getTimeStamp());
164 Real32 frame_time
= OSG::getTimeStampMsecs(cur_time
-mLastFrameStart
);
165 mLastFrameStart
= cur_time
;
166 mFrameTimes
.push_front(frame_time
);
167 while (mFrameTimes
.size() > max_samples
)
169 mFrameTimes
.pop_back();
171 Real32 av_frame_time
= std::accumulate(mFrameTimes
.begin(),
172 mFrameTimes
.end(), 0.0)
173 / static_cast<Real32
>(mFrameTimes
.size());
174 mFrameRate
= 1.0 / av_frame_time
;
177 std::string
NestedPerfTracker::outString(bool detailed
)
179 std::string ret_val
= mPerfRoot
->outString(1, detailed
);
183 NestedPerfTracker::sample_pair_vector_t
NestedPerfTracker::getFlatSampleTree(
185 NestedSampleInfoPtr curNode
188 NestedPerfTracker::sample_pair_vector_t sample_list
;
189 if (curNode
.get() == NULL
)
193 sample_list
.push_back(SamplePair(depth
, curNode
));
195 typedef NestedSampleInfo::subsample_map_t::iterator iter_type
;
196 for(iter_type i
= curNode
->mSubSamples
.begin();
197 i
!= curNode
->mSubSamples
.end(); ++i
)
199 NestedPerfTracker::sample_pair_vector_t sub_sample_list
=
200 getFlatSampleTree(depth
+1, (*i
).second
);
201 sample_list
.insert(sample_list
.end(),
202 sub_sample_list
.begin(), sub_sample_list
.end());
207 Real32
NestedPerfTracker::getFrameRate(UInt32 avgOverFrames
) const
209 if (0 == avgOverFrames
)
215 avgOverFrames
= OSG::osgMin(avgOverFrames
,
216 static_cast<UInt32
>(mFrameTimes
.size()));
217 Real32 av_frame_time
=
218 std::accumulate(mFrameTimes
.begin(),
219 mFrameTimes
.begin() + avgOverFrames
, 0.0)
220 / static_cast<Real32
>(avgOverFrames
);
221 Real32 frame_rate
= 1.0 / av_frame_time
;
226 UInt32
NestedPerfTracker::max_samples
= 500;
229 PerfMonitorBase::PerfMonitorBase()
233 , mCurrentFrameNum(0)
238 mDataLock
= OSG::Lock::get("PerfMonitor::mDataLock", false);
239 mStartTimeMs
= OSG::getTimeStampMsecs(OSG::getTimeStamp());
242 PerfMonitorBase::~PerfMonitorBase(void)
247 void PerfMonitorBase::reset()
249 mDataLock
->acquire();
251 setOutputFile(""); // Clear the output file
253 mDataLock
->release();
256 void PerfMonitorBase::enable(bool val
)
261 void PerfMonitorBase::sample(SampleType sampleType
, const std::string
& name
,
262 OSG::Real32 metricValue
, const std::string
& desc
)
269 OSG::Real64 sample_time
= getTimeStampMs();
270 OSG::UInt64 tid
= getThreadId();
272 SampleData
sample_data(tid
, sample_time
, sampleType
, name
, metricValue
,
275 // XXX: This is expensive. We need a better way to do this
276 // that allows for lock-free collection of samples.
277 mDataLock
->acquire();
278 mSamples
.push_back(sample_data
);
279 mDataLock
->release();
281 // TODO: Update helpers
285 /** Called once per "frame" to update internal data structures. */
286 void PerfMonitorBase::updateFrame()
288 mCurrentFrameNum
+= 1;
289 sample(FRAME
, "frame", mCurrentFrameNum
);
291 mDataLock
->acquire();
293 // TODO: Update helpers
296 if (mSamples
.size() >= mFlushingRate
)
302 mDataLock
->release();
305 /** Return the id of the current thread. */
306 OSG::UInt64
PerfMonitorBase::getThreadId() const
309 return OSG::UInt64(GetCurrentThreadId());
311 return OSG::UInt64(pthread_self());
315 void PerfMonitorBase::setOutputFile(const std::string
& filename
)
317 // Close up old file if needed
318 if (mOutFile
!= NULL
)
327 // Open the file if needed
328 mOutFile
= new std::ofstream(filename
.c_str(), std::ios_base::trunc
);
332 void PerfMonitorBase::flushOutput()
334 if (NULL
== mOutFile
)
339 for(UInt32 i
= 0; i
< mSamples
.size(); ++i
)
341 SampleData cur_sample
= mSamples
[i
];
342 (*mOutFile
) << cur_sample
.thread_id
<< ","
343 << std::setprecision(20) << std::fixed
344 << cur_sample
.timestamp
<< ","
345 << static_cast<UInt32
>(cur_sample
.type
) << ","
346 << std::scientific
<< std::setprecision(20)
347 << cur_sample
.metric_value
<< ","
348 << '"' << cur_sample
.name
<< '"' << ","
349 << '"' << cur_sample
.desc
<< '"' << std::endl
;
355 void PerfMonitorBase::printSamples()
357 for(UInt32 i
= 0; i
< mSamples
.size(); ++i
)
359 SampleData cur_sample
= mSamples
[i
];
360 std::cout
<< i
<< ": "
361 << " tid: " << cur_sample
.thread_id
362 << " ts: " << cur_sample
.timestamp
363 << " type: " << static_cast<UInt32
>(cur_sample
.type
)
364 << " metric: " << cur_sample
.metric_value
365 << " name: " << cur_sample
.name
366 << " desc: " << cur_sample
.desc
<< std::endl
;