1 /*
2 Minetest
3 Copyright (C) 2015 celeron55, Perttu Ahola <celeron55@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include "profiler.h"
21 #include "porting.h"
22 
23 static Profiler main_profiler;
24 Profiler *g_profiler = &main_profiler;
ScopeProfiler(Profiler * profiler,const std::string & name,ScopeProfilerType type)25 ScopeProfiler::ScopeProfiler(
26 		Profiler *profiler, const std::string &name, ScopeProfilerType type) :
27 		m_profiler(profiler),
28 		m_name(name), m_type(type)
29 {
30 	m_name.append(" [ms]");
31 	if (m_profiler)
32 		m_timer = new TimeTaker(m_name, nullptr, PRECISION_MILLI);
33 }
34 
~ScopeProfiler()35 ScopeProfiler::~ScopeProfiler()
36 {
37 	if (!m_timer)
38 		return;
39 
40 	float duration_ms = m_timer->stop(true);
41 	float duration = duration_ms;
42 	if (m_profiler) {
43 		switch (m_type) {
44 		case SPT_ADD:
45 			m_profiler->add(m_name, duration);
46 			break;
47 		case SPT_AVG:
48 			m_profiler->avg(m_name, duration);
49 			break;
50 		case SPT_GRAPH_ADD:
51 			m_profiler->graphAdd(m_name, duration);
52 			break;
53 		}
54 	}
55 	delete m_timer;
56 }
57 
Profiler()58 Profiler::Profiler()
59 {
60 	m_start_time = porting::getTimeMs();
61 }
62 
add(const std::string & name,float value)63 void Profiler::add(const std::string &name, float value)
64 {
65 	MutexAutoLock lock(m_mutex);
66 	{
67 		/* No average shall have been used; mark add used as -2 */
68 		std::map<std::string, int>::iterator n = m_avgcounts.find(name);
69 		if (n == m_avgcounts.end()) {
70 			m_avgcounts[name] = -2;
71 		} else {
72 			if (n->second == -1)
73 				n->second = -2;
74 			assert(n->second == -2);
75 		}
76 	}
77 	{
78 		std::map<std::string, float>::iterator n = m_data.find(name);
79 		if (n == m_data.end())
80 			m_data[name] = value;
81 		else
82 			n->second += value;
83 	}
84 }
85 
avg(const std::string & name,float value)86 void Profiler::avg(const std::string &name, float value)
87 {
88 	MutexAutoLock lock(m_mutex);
89 	int &count = m_avgcounts[name];
90 
91 	assert(count != -2);
92 	count = MYMAX(count, 0) + 1;
93 	m_data[name] += value;
94 }
95 
clear()96 void Profiler::clear()
97 {
98 	MutexAutoLock lock(m_mutex);
99 	for (auto &it : m_data) {
100 		it.second = 0;
101 	}
102 	m_avgcounts.clear();
103 	m_start_time = porting::getTimeMs();
104 }
105 
getValue(const std::string & name) const106 float Profiler::getValue(const std::string &name) const
107 {
108 	auto numerator = m_data.find(name);
109 	if (numerator == m_data.end())
110 		return 0.f;
111 
112 	auto denominator = m_avgcounts.find(name);
113 	if (denominator != m_avgcounts.end()) {
114 		if (denominator->second >= 1)
115 			return numerator->second / denominator->second;
116 	}
117 
118 	return numerator->second;
119 }
120 
getAvgCount(const std::string & name) const121 int Profiler::getAvgCount(const std::string &name) const
122 {
123 	auto n = m_avgcounts.find(name);
124 
125 	if (n != m_avgcounts.end() && n->second >= 1)
126 		return n->second;
127 
128 	return 1;
129 }
130 
getElapsedMs() const131 u64 Profiler::getElapsedMs() const
132 {
133 	return porting::getTimeMs() - m_start_time;
134 }
135 
print(std::ostream & o,u32 page,u32 pagecount)136 int Profiler::print(std::ostream &o, u32 page, u32 pagecount)
137 {
138 	GraphValues values;
139 	getPage(values, page, pagecount);
140 	char num_buf[50];
141 
142 	for (const auto &i : values) {
143 		o << "  " << i.first << " ";
144 		if (i.second == 0) {
145 			o << std::endl;
146 			continue;
147 		}
148 
149 		s32 space = 44 - i.first.size();
150 		for (s32 j = 0; j < space; j++) {
151 			if ((j & 1) && j < space - 1)
152 				o << ".";
153 			else
154 				o << " ";
155 		}
156 		porting::mt_snprintf(num_buf, sizeof(num_buf), "% 4ix % 3g",
157 				getAvgCount(i.first), i.second);
158 		o << num_buf << std::endl;
159 	}
160 	return values.size();
161 }
162 
getPage(GraphValues & o,u32 page,u32 pagecount)163 void Profiler::getPage(GraphValues &o, u32 page, u32 pagecount)
164 {
165 	MutexAutoLock lock(m_mutex);
166 
167 	u32 minindex, maxindex;
168 	paging(m_data.size(), page, pagecount, minindex, maxindex);
169 
170 	for (const auto &i : m_data) {
171 		if (maxindex == 0)
172 			break;
173 		maxindex--;
174 
175 		if (minindex != 0) {
176 			minindex--;
177 			continue;
178 		}
179 
180 		o[i.first] = i.second / getAvgCount(i.first);
181 	}
182 }
183