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