1 /*
2 This file is part of GNU APL, a free implementation of the
3 ISO/IEC Standard 13751, "Programming Language APL, Extended"
4
5 Copyright (C) 2008-2015 Dr. Jürgen Sauermann
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <iomanip>
22
23 #include <math.h>
24
25 #include "Common.hh"
26 #include "Performance.hh"
27 #include "PrintOperator.hh"
28 #include "UCS_string.hh"
29
30 /* Note:
31 perfo_1: monadic cell statistics
32 perfo_2: dyadic cell statistics
33 perfo_3: monadic function statistics
34 perfo_4: dyadic function statistics
35 */
36
37 #define perfo_1(id, ab, _name, _thr) \
38 CellFunctionStatistics Performance::cfs_ ## id ## ab(PFS_## id ## ab);
39 #define perfo_2(id, ab, _name, _thr) \
40 CellFunctionStatistics Performance::cfs_ ## id ## ab(PFS_ ## id ## ab);
41 #define perfo_3(id, ab, _name, _thr) \
42 FunctionStatistics Performance::fs_ ## id ## ab (PFS_ ## id ## ab);
43 #define perfo_4(id, ab, _name, _thr) \
44 FunctionStatistics Performance::fs_ ## id ## ab (PFS_ ## id ## ab);
45 #include "Performance.def"
46
47 //----------------------------------------------------------------------------
~Statistics()48 Statistics::~Statistics()
49 {
50 }
51 //----------------------------------------------------------------------------
52 Statistics *
get_statistics(Pfstat_ID id)53 Performance::get_statistics(Pfstat_ID id)
54 {
55 switch(id)
56 {
57 #define perfo_1(id, ab, _name, _thr) \
58 case PFS_ ## id ## ab: return &cfs_ ## id ## ab;
59 #define perfo_2(id, ab, _name, _thr) \
60 case PFS_ ## id ## ab: return &cfs_ ## id ## ab;
61 #define perfo_3(id, ab, name, _thr) \
62 case PFS_ ## id ## ab: return &fs_ ## id ## ab;
63 #define perfo_4(id, ab, name, _thr) \
64 case PFS_ ## id ## ab: return &fs_ ## id ## ab;
65 #include "Performance.def"
66 default: return 0;
67 }
68
69 // not reached
70 return 0;
71 }
72 //----------------------------------------------------------------------------
73 int
get_statistics_type(Pfstat_ID id)74 Performance::get_statistics_type(Pfstat_ID id)
75 {
76 switch(id)
77 {
78 #define perfo_1(id, ab, _name, _thr) case PFS_ ## id ## ab: return 1;
79 #define perfo_2(id, ab, _name, _thr) case PFS_ ## id ## ab: return 2;
80 #define perfo_3(id, ab, _name, _thr) case PFS_ ## id ## ab: return 3;
81 #define perfo_4(id, ab, _name, _thr) case PFS_ ## id ## ab: return 3;
82 #include "Performance.def"
83 default: return 0;
84 }
85
86 // not reached
87 return 0;
88 }
89 //----------------------------------------------------------------------------
90 const char *
get_name(Pfstat_ID id)91 Statistics::get_name(Pfstat_ID id)
92 {
93 switch(id)
94 {
95 #define perfo_1(id, ab, name, _thr) case PFS_ ## id ## ab: return name;
96 #define perfo_2(id, ab, name, _thr) case PFS_ ## id ## ab: return name;
97 #define perfo_3(id, ab, name, _thr) case PFS_ ## id ## ab: return name;
98 #define perfo_4(id, ab, name, _thr) case PFS_ ## id ## ab: return name;
99 #include "Performance.def"
100 case PFS_SCALAR_B_overhead: return " f B+overhead";
101 case PFS_SCALAR_AB_overhead: return "A f B+overhead";
102 default: return "Unknown Pfstat_ID";
103 }
104
105 // not reached
106 return 0;
107 }
108 //----------------------------------------------------------------------------
109 void
print(Pfstat_ID which,ostream & out)110 Performance::print(Pfstat_ID which, ostream & out)
111 {
112 if (which != PFS_ALL) // individual statistics
113 {
114 Statistics * stat = get_statistics(which);
115 if (!stat)
116 {
117 out << "No such statistics: " << which << endl;
118 return;
119 }
120
121 if (which < PFS_MAX2) // cell function statistics
122 {
123 out <<
124 "╔═════════════════╦══════════╤═════════╤════════╦══════════╤═════════╤════════╗"
125 << endl;
126 stat->print(out);
127 out <<
128 "╚═════════════════╩══════════╧═════════╧════════╩══════════╧═════════╧════════╝"
129 << endl;
130 }
131 else
132 {
133 out <<
134 "╔═════════════════╦════════════╤══════════╤══════════╤══════════╤══════════╗"
135 << endl;
136 stat->print(out);
137 out <<
138 "╚═════════════════╩════════════╧══════════╧══════════╧══════════╧══════════╝"
139 << endl;
140 }
141 return;
142 }
143
144 // print all statistics and compute column sums
145 //
146 uint64_t sum_first_N_B = 0;
147 uint64_t sum_first_cycles_B = 0;
148 uint64_t sum_subsq_N_B = 0;
149 uint64_t sum_subsq_cycles_B = 0;
150 uint64_t sum_first_N_AB = 0;
151 uint64_t sum_first_cycles_AB = 0;
152 uint64_t sum_subsq_N_AB = 0;
153 uint64_t sum_subsq_cycles_AB = 0;
154
155 out <<
156 "╔═════════════════════════════════════════════════════════════════════╗\n"
157 "║ Performance Statistics (CPU cycles per vector item) ║\n"
158 "╠═════════════════╦═════════════════════════╦═════════════════════════╣\n"
159 "║ ║ first pass ║ subsequent passes ║\n"
160 "║ Cell ╟───────┬───────┬─────────╫───────┬───────┬─────────╢\n"
161 "║ Function ║ N │⌀cycles│ σ÷μ % ║ N │⌀cycles│ σ÷μ % ║\n"
162 "╠═════════════════╬═══════╪═══════╪═════════╬═══════╪═══════╪═════════╣\n";
163
164
165 #define perfo_1(id, ab, _name, _thr) \
166 cfs_ ## id ## ab.print(out); \
167 sum_first_N_B += cfs_ ## id ## ab.get_first_record()->get_count(); \
168 sum_first_cycles_B += cfs_ ## id ## ab.get_first_record()->get_sum(); \
169 sum_subsq_N_B += cfs_ ## id ## ab.get_record()->get_count(); \
170 sum_subsq_cycles_B += cfs_ ## id ## ab.get_record()->get_sum();
171
172 #define perfo_2(id, ab, name, thr) \
173 cfs_ ## id ## ab.print(out); \
174 sum_first_N_AB += cfs_ ## id ## ab.get_first_record()->get_count(); \
175 sum_first_cycles_AB += cfs_ ## id ## ab.get_first_record()->get_sum(); \
176 sum_subsq_N_AB += cfs_ ## id ## ab.get_record()->get_count(); \
177 sum_subsq_cycles_AB += cfs_ ## id ## ab.get_record()->get_sum();
178 #define perfo_3(id, ab, _name, _thr)
179 #define perfo_4(id, ab, _name, _thr)
180 #include "Performance.def"
181
182 const uint64_t first_avg_B = Statistics_record::average(sum_first_cycles_B,
183 sum_first_N_B);
184 const uint64_t subsq_avg_B = Statistics_record::average(sum_subsq_cycles_B,
185 sum_subsq_N_B);
186
187 out <<
188 "╟─────────────────╫───────┼───────┼─────────╫───────┼───────┼─────────╢\n"
189 "║ SUM B ║ ";
190 Statistics_record::print5(out, sum_first_N_B);
191 out << " │ ";
192 Statistics_record::print5(out, first_avg_B);
193 out << " │ ║ ";
194 Statistics_record::print5(out, sum_subsq_N_B);
195 out << " │ ";
196 Statistics_record::print5(out, subsq_avg_B);
197 out << " │ ║\n";
198
199 const uint64_t first_avg_AB = Statistics_record::average(sum_first_cycles_AB,
200 sum_first_N_AB);
201 const uint64_t subsq_avg_AB = Statistics_record::average(sum_subsq_cycles_AB,
202 sum_subsq_N_AB);
203 out <<
204 "║ SUM AB ║ ";
205 Statistics_record::print5(out, sum_first_N_AB);
206 out << " │ ";
207 Statistics_record::print5(out, first_avg_AB);
208 out << " │ ║ ";
209 Statistics_record::print5(out, sum_subsq_N_AB);
210 out << " │ ";
211 Statistics_record::print5(out, subsq_avg_AB);
212 out << " │ ║\n";
213
214 out <<
215 "╚═════════════════╩═══════╧═══════╧═════════╩═══════╧═══════╧═════════╝\n"
216 "╔═════════════════╦═══════════════╤═══════════════╤═══════╗\n"
217 "║ Function ║ Total │ Average │Cycles ║\n"
218 "║ or ╟───────┬───────┼───────┬───────┤ per ║\n"
219 "║ Operation ║ N │Cycles │ Items │Cycles │ Item ║\n"
220 "╟─────────────────╫───────┼───────┼───────┼───────┼───────╢"
221 << endl;
222
223 // other statistics...
224 //
225 #define perfo_1(id, ab, _name, _thr)
226 #define perfo_2(id, ab, _name, _thr)
227 #define perfo_3(id, ab, _name, _thr) fs_ ## id ## ab.print(out);
228 #define perfo_4(id, ab, name, thr) perfo_3(id, ab, name, thr)
229
230 #include "Performance.def"
231
232 out <<
233 "╚═════════════════╩═══════╧═══════╧═══════╧═══════╧═══════╝"
234 << endl;
235 }
236 //----------------------------------------------------------------------------
237 void
save_data(ostream & out,ostream & out_file)238 Performance::save_data(ostream & out, ostream & out_file)
239 {
240 #define perfo_1(id, ab, _name, _thr) cfs_ ## id ## ab.save_data(out_file, #id);
241 #define perfo_2(id, ab, _name, _thr) cfs_ ## id ## ab.save_data(out_file, #id);
242 #define perfo_3(id, ab, _name, _thr) fs_ ## id ## ab.save_data(out_file, #id);
243 #define perfo_4(id, ab, _name, _thr) fs_ ## id ## ab.save_data(out_file, #id);
244 #include "Performance.def"
245 }
246 //----------------------------------------------------------------------------
247 void
reset_all()248 Performance::reset_all()
249 {
250 #define perfo_1(id, ab, _name, _thr) cfs_ ## id ## ab.reset();
251 #define perfo_2(id, ab, _name, _thr) cfs_ ## id ## ab.reset();
252 #define perfo_3(id, ab, _name, _thr) fs_ ## id ## ab.reset();
253 #define perfo_4(id, ab, _name, _thr) fs_ ## id ## ab.reset();
254 #include "Performance.def"
255 }
256 //----------------------------------------------------------------------------
257 void
print(ostream & out)258 Statistics_record::print(ostream & out)
259 {
260 uint64_t mu = 0;
261 int sigma_percent = 0;
262
263 if (count)
264 {
265 mu = data/count;
266 const double sigma = sqrt(data2/count - mu*mu);
267 sigma_percent = int((sigma/mu)*100);
268 }
269
270 print5(out, count);
271 out << " │ "; print5(out, mu);
272 out << " │ "; print5(out, sigma_percent);
273 out << " %";
274 }
275 //----------------------------------------------------------------------------
276 void
save_record(ostream & outf)277 Statistics_record::save_record(ostream & outf)
278 {
279 uint64_t mu = 0;
280 double sigma = 0;
281 if (count)
282 {
283 mu = data/count;
284 sigma = sqrt(data2/count - mu*mu);
285 }
286
287 outf << setw(8) << count << ","
288 << setw(8) << mu << ","
289 << setw(8) << uint64_t(sigma + 0.5);
290 }
291 //----------------------------------------------------------------------------
292 void
print5(ostream & out,uint64_t num)293 Statistics_record::print5(ostream & out, uint64_t num)
294 {
295 char cc[40];
296 if (num < 100000) // special case: no multiplier
297 {
298 snprintf(cc, sizeof(cc), "%5u", uint32_t(num));
299 out << cc;
300 return;
301 }
302
303 // kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta, Xona, Weka, Vunda, Una
304 // 1E3 1E6 1E9 1E12 1E15 1E18
305 // max uint64_t is 1.8E19 = 18 Exa
306 //
307 const char * units = "-kMGTPE??????";
308 double fnum = num;
309 while (fnum > 1000.0)
310 {
311 ++units;
312 fnum = fnum / 1000.0;
313 }
314
315 snprintf(cc, sizeof(cc), "%f", fnum);
316 if (cc[3] == '.') { cc[3] = 0; out << " " << cc << *units; }
317 else { cc[4] = 0; out << cc << *units; }
318 }
319 //============================================================================
320 void
print(ostream & out)321 FunctionStatistics::print(ostream & out)
322 {
323 UTF8_string utf(get_name());
324 UCS_string uname(utf);
325 out << "║ " << utf;
326 loop(n, 15 - uname.size()) out << " ";
327
328 const uint64_t div = vec_lengths.get_average() ? vec_lengths.get_average() : 1;
329
330 out << " ║ "; Statistics_record::print5(out, vec_lengths.get_count());
331 out << " │ "; Statistics_record::print5(out, vec_cycles.get_sum());
332 out << " │ "; Statistics_record::print5(out, vec_lengths.get_average());
333 out << " │ "; Statistics_record::print5(out, vec_cycles.get_average());
334 out << " │ "; Statistics_record::print5(out, vec_cycles.get_average()/div);
335 out << " ║" << endl;
336 }
337 //----------------------------------------------------------------------------
338 void
save_data(ostream & outf,const char * perf_name)339 FunctionStatistics::save_data(ostream & outf, const char * perf_name)
340 {
341 char cc[100];
342 snprintf(cc, sizeof(cc), "%s,", perf_name);
343 outf << "prf_3 (PFS_" << left << setw(12) << cc << right;
344 vec_cycles.save_record(outf);
345 outf << ")" << endl;
346 }
347 //============================================================================
348 void
print(ostream & out)349 CellFunctionStatistics::print(ostream & out)
350 {
351 UTF8_string utf(get_name());
352 UCS_string uname(utf);
353 out << "║ " << uname;
354 loop(n, 12 - uname.size()) out << " ";
355 out << " ║ ";
356
357 first.print(out);
358 out << " ║ ";
359 subsequent.print(out);
360 out << " ║" << endl;
361 }
362 //----------------------------------------------------------------------------
363 void
save_data(ostream & outf,const char * perf_name)364 CellFunctionStatistics::save_data(ostream & outf, const char * perf_name)
365 {
366 char cc[100];
367 snprintf(cc, sizeof(cc), "%s,", perf_name);
368 outf << "prf_12(PFS_" << left << setw(12) << cc << right;
369 first.save_record(outf);
370 outf << ",";
371 subsequent.save_record(outf);
372 outf << ")" << endl;
373 }
374 //============================================================================
375
376