1 /*========================== begin_copyright_notice ============================
2 
3 Copyright (C) 2019-2021 Intel Corporation
4 
5 SPDX-License-Identifier: MIT
6 
7 ============================= end_copyright_notice ===========================*/
8 
9 #pragma once
10 
11 #if defined( _INTERNAL) || defined( _DEBUG )
12     #define COMPILER_STATS_ENABLE 1
13 #endif
14 
15 #include <cassert>
16 #include <unordered_map>
17 #include <map>
18 #include <sstream>
19 #include <memory>
20 
21 class CompilerStats {
22     // Internal class for storing data. Every statistic knows its type and keeps values for
23     // each simd size (potenatially wastes memory but improves execution time).
24     struct Statistic
25     {
26         // Supported data types:
27         enum Type
28         {
29             type_bool,
30             type_int64,
31             type_double
32         };
33 
34         // Supported simd sizes:
35         enum SimdType
36         {
37             SIMD_GENERIC = 0,
38             SIMD_8 = 1,
39             SIMD_16 = 2,
40             SIMD_32 = 3,
41             NUM_SIMD_TYPES
42         };
43 
44         // Store every type as 64-bit value.
45         union Value
46         {
47             uint64_t    value = 0;
48             bool        bool_value;
49             int64_t     int64_value;
50             double      double_value;
51         };
52 
53         // Helper function to convert from SimdType enum to integer represenation (8/16/32). Returns 0 for generic simd.
54         inline static int type_to_simd(SimdType simd_type);
55 
56         // Helper function to convert from integer;
57         inline static SimdType simd_to_type(int simd);
58 
59         // Get stat value for simd size expressed as integer. Pass 0 to access generic.
60         inline Value& operator[](int simd);
61         inline const Value& operator[](int simd) const;
62 
63         // Members:
64         Type    type;
65         Value   values[NUM_SIMD_TYPES];
66     };
67 
68     // Create empty statistic if does not exist, return existing if it does.
69     inline Statistic& PrivateInit(const std::string& name, Statistic::Type type, int simd);
70 
71     // Returns true if the statistic name exists in the map.
72     inline bool PrivateFind(const std::string& name);
73 
74     // Try to find statistic. Return empty one if it does not exist.
75     inline const Statistic& PrivateGet(const std::string& name, int simd) const;
76 
IsEnabled()77     inline bool IsEnabled() const { return m_pState != nullptr; }
78 
79     struct State {
80         std::unordered_map<std::string, Statistic> m_Stats;
81         bool m_CollectOnlyInitialized = true;
82     };
83 
84     std::shared_ptr<State> m_pState;
85     Statistic m_ZeroStat;
86 
87 public:
88     static constexpr Statistic::Type type_bool     = Statistic::Type::type_bool;
89     static constexpr Statistic::Type type_int64    = Statistic::Type::type_int64;
90     static constexpr Statistic::Type type_double   = Statistic::Type::type_double;
91 
numSendStr()92     static constexpr const char* numSendStr() { return "NumSendInst"; };
numGRFSpillStr()93     static constexpr const char* numGRFSpillStr() { return "NumGRFSpill"; };
numGRFFillStr()94     static constexpr const char* numGRFFillStr() { return "NumGRFFill"; };
numCyclesStr()95     static constexpr const char* numCyclesStr() { return "NumCycles"; };
96 
97 
98     // Statistic collection is disabled by default.
99     inline void Enable(bool collectOnlyInitialized);
100 
101     // Link statistics to the one from the provided object.
102     inline void Link(CompilerStats& sharedData);
103 
104     // Merge the given Compiler Stats
105     inline void MergeStats(CompilerStats& from, int simd);
106 
107     // Create statistic with value set to zero/false.
108     inline void Init(const std::string& name, Statistic::Type type, int simd=0);
109 
110     // Set value to zero/false.
111     inline void Reset(const std::string& name, int simd = 0);
112 
113     // Return true if the statistic name exists.
114     inline bool Find(const std::string& name);
115 
116     // Set flag statistic to true.
117     inline void SetFlag(const std::string& name, int simd=0);
118 
119     // Set value of integer statistic.
120     inline void SetI64(const std::string& name, int64_t value, int simd=0);
121 
122     // Set value of floating point statistic.
123     inline void SetF64(const std::string& name, double value, int simd=0);
124 
125     // Increase value of integer statistic.
126     inline void IncreaseI64(const std::string& name, int64_t value, int simd=0);
127 
128     // Increase value of floating point statistic.
129     inline void IncreaseF64(const std::string& name, double value, int simd=0);
130 
131     // Get value of a flag. Returns false if not found.
132     inline bool GetFlag(const std::string& name, int simd=0) const;
133 
134     // Get value of an integer. Returns 0 if not found.
135     inline int64_t GetI64(const std::string& name, int simd=0) const;
136 
137     // Get value of a floating point. Returns 0.0 if not found.
138     inline double GetF64(const std::string& name, int simd=0) const;
139 
140     // Dump stats to csv format string.
141     inline std::string ToCsv() const;
142 };
143 
144 // Enable collection of statistics.
145 // collectOnlyInitialized - if true, statistic initialization is required, otherwise statistic update will be ignored
Enable(bool collectOnlyInitialized)146 void CompilerStats::Enable(bool collectOnlyInitialized)
147 {
148     if (!IsEnabled())
149     {
150         m_pState = std::make_shared<State>();
151         m_pState->m_CollectOnlyInitialized = collectOnlyInitialized;
152     }
153 }
154 
155 // Link statistics to the one from the provided object.
Link(CompilerStats & sharedData)156 void CompilerStats::Link(CompilerStats& sharedData)
157 {
158     if (m_pState != nullptr)
159     {
160         m_pState.reset();
161     }
162     m_pState = sharedData.m_pState;
163 }
164 
MergeStats(CompilerStats & from,int simd)165 void CompilerStats::MergeStats(CompilerStats& from, int simd)
166 {
167     for (auto elem : from.m_pState->m_Stats)
168     {
169         auto name = elem.first;
170         auto type = elem.second.type;
171         auto f = m_pState->m_Stats.find(name);
172         if (f == m_pState->m_Stats.end())
173         {
174             Statistic& s = PrivateInit(name, type, simd);
175             s[simd].value = elem.second[simd].value;
176         }
177         else
178         {
179             assert(type == f->second.type);
180             // If flag, do OR rather than overwrite. This is because we want
181             // to preserve the bool stats from an earlier try at this simd.
182             if (type == Statistic::type_bool)
183             {
184                 f->second[simd].value |= elem.second[simd].value;
185             }
186             else
187             {
188                 f->second[simd].value = elem.second[simd].value;
189             }
190         }
191     }
192 }
193 
194 // Create statistic with value set to zero/false.
Init(const std::string & name,Statistic::Type type,int simd)195 void CompilerStats::Init(const std::string& name, Statistic::Type type, int simd)
196 {
197     if (IsEnabled())
198     {
199         auto f = m_pState->m_Stats.find(name);
200         if(f == m_pState->m_Stats.end())
201         {
202             PrivateInit(name, type, simd);
203         }
204     }
205 }
206 
207 // Set value to zero/false.
Reset(const std::string & name,int simd)208 void CompilerStats::Reset(const std::string& name, int simd)
209 {
210     if (IsEnabled())
211     {
212         auto f = m_pState->m_Stats.find(name);
213         if(f != m_pState->m_Stats.end())
214         {
215             f->second[simd].value = 0;
216         }
217     }
218 }
219 
220 // Return true if the statistic name exists.
Find(const std::string & name)221 bool CompilerStats::Find(const std::string& name)
222 {
223     return PrivateFind(name);
224 }
225 
226 // Set flag statistic to true.
227 // If statistic does not exist, create new one.
SetFlag(const std::string & name,int simd)228 void CompilerStats::SetFlag(const std::string& name, int simd)
229 {
230     if (IsEnabled())
231     {
232         auto f = m_pState->m_Stats.find(name);
233         if (f == m_pState->m_Stats.end())
234         {
235             if (!m_pState->m_CollectOnlyInitialized)
236             {
237                 Statistic& s = PrivateInit(name, Statistic::type_bool, simd);
238                 s[simd].bool_value = true;
239             }
240         }
241         else
242         {
243             Statistic& s = f->second;
244             assert(s.type == Statistic::type_bool);
245             s[simd].bool_value = true;
246         }
247     }
248 }
249 
250 // Set value of integer statistic.
251 // If statistic does not exist, create new one.
SetI64(const std::string & name,int64_t value,int simd)252 void CompilerStats::SetI64(const std::string& name, int64_t value, int simd)
253 {
254     if (IsEnabled())
255     {
256         auto f = m_pState->m_Stats.find(name);
257         if (f == m_pState->m_Stats.end())
258         {
259             if (!m_pState->m_CollectOnlyInitialized)
260             {
261                 Statistic& s = PrivateInit(name, Statistic::type_int64, simd);
262                 s[simd].int64_value = value;
263             }
264         }
265         else
266         {
267             Statistic& s = f->second;
268             assert(s.type == Statistic::type_int64);
269             s[simd].int64_value = value;
270         }
271     }
272 }
273 
274 // Set value of floating point statistic.
275 // If statistic does not exist, create new one.
SetF64(const std::string & name,double value,int simd)276 void CompilerStats::SetF64(const std::string& name, double value, int simd)
277 {
278     if (IsEnabled())
279     {
280         auto f = m_pState->m_Stats.find(name);
281         if (f == m_pState->m_Stats.end())
282         {
283             if (!m_pState->m_CollectOnlyInitialized)
284             {
285                 Statistic& s = PrivateInit(name, Statistic::type_double, simd);
286                 s[simd].double_value = value;
287             }
288         }
289         else
290         {
291             Statistic& s = f->second;
292             assert(s.type == Statistic::type_double);
293             s[simd].double_value = value;
294         }
295     }
296 }
297 
298 // Increase value of integer statistic.
299 // If statistic does not exist, create new one.
IncreaseI64(const std::string & name,int64_t value,int simd)300 void CompilerStats::IncreaseI64(const std::string& name, int64_t value, int simd)
301 {
302     if (IsEnabled())
303     {
304         auto f = m_pState->m_Stats.find(name);
305         if (f == m_pState->m_Stats.end())
306         {
307             if (!m_pState->m_CollectOnlyInitialized)
308             {
309                 Statistic& s = PrivateInit(name, Statistic::type_int64, simd);
310                 s[simd].int64_value += value;
311             }
312         }
313         else
314         {
315             Statistic& s = f->second;
316             assert(s.type == Statistic::type_int64);
317             s[simd].int64_value += value;
318         }
319     }
320 }
321 
322 // Increase value of floating point statistic.
323 // If statistic does not exist, create new one.
IncreaseF64(const std::string & name,double value,int simd)324 void CompilerStats::IncreaseF64(const std::string& name, double value, int simd)
325 {
326     if (IsEnabled())
327     {
328         auto f = m_pState->m_Stats.find(name);
329         if (f == m_pState->m_Stats.end())
330         {
331             if (!m_pState->m_CollectOnlyInitialized)
332             {
333                 Statistic& s = PrivateInit(name, Statistic::type_double, simd);
334                 s[simd].double_value += value;
335             }
336         }
337         else
338         {
339             Statistic& s = f->second;
340             assert(s.type == Statistic::type_double);
341             s[simd].double_value += value;
342         }
343     }
344 }
345 
346 // Get value of a flag. Returns false if not found.
GetFlag(const std::string & name,int simd)347 bool CompilerStats::GetFlag(const std::string& name, int simd) const
348 {
349     return PrivateGet(name, simd)[simd].bool_value;
350 }
351 
352 // Get value of an integer. Returns 0 if not found.
GetI64(const std::string & name,int simd)353 int64_t CompilerStats::GetI64(const std::string& name, int simd) const
354 {
355     return PrivateGet(name, simd)[simd].int64_value;
356 }
357 
358 // Get value of a floating point. Returns 0.0 if not found.
GetF64(const std::string & name,int simd)359 double CompilerStats::GetF64(const std::string& name, int simd) const
360 {
361     return PrivateGet(name, simd)[simd].double_value;
362 }
363 
364 // Dump stats to csv format string.
ToCsv()365 std::string CompilerStats::ToCsv() const
366 {
367     if (IsEnabled() == false)
368         return "";
369 
370     std::stringstream ss;
371     ss << "Name,Generic,Simd8,Simd16,Simd32,\n";
372     std::map<std::string, Statistic> ordered(m_pState->m_Stats.begin(), m_pState->m_Stats.end());
373     for (auto elem : ordered)
374     {
375         ss << elem.first << ",";
376         for(int simd_type = 0; simd_type < Statistic::SimdType::NUM_SIMD_TYPES; simd_type++)
377         {
378             switch (elem.second.type)
379             {
380                 case Statistic::Type::type_bool:
381                     ss << elem.second.values[simd_type].bool_value << ",";
382                     break;
383                 case Statistic::Type::type_int64:
384                     ss << elem.second.values[simd_type].int64_value << ",";
385                     break;
386                 case Statistic::Type::type_double:
387                     ss << elem.second.values[simd_type].double_value << ",";
388                     break;
389             }
390         }
391         ss << "\n";
392     }
393     return ss.str();
394 }
395 
396 // Create empty statistic if does not exist, return existing if it does.
PrivateInit(const std::string & name,Statistic::Type type,int simd)397 CompilerStats::Statistic& CompilerStats::PrivateInit(const std::string& name, Statistic::Type type, int simd)
398 {
399     Statistic s;
400     s.type = type;
401     m_pState->m_Stats[name] = s;
402     return m_pState->m_Stats[name];
403 }
404 
405 // Checks if the statistic name exists in the map
PrivateFind(const std::string & name)406 bool CompilerStats::PrivateFind(const std::string& name)
407 {
408     if (IsEnabled())
409     {
410         return m_pState->m_Stats.find(name) != m_pState->m_Stats.end();
411     }
412     return false;
413 }
414 
415 // Try to find statistic. Return empty one if it does not exist.
PrivateGet(const std::string & name,int simd)416 const CompilerStats::Statistic& CompilerStats::PrivateGet(const std::string& name, int simd) const
417 {
418     auto f = m_pState->m_Stats.find(name);
419     if(f != m_pState->m_Stats.end())
420     {
421         return f->second;
422     }
423     return m_ZeroStat;
424 }
425 
426 
427 // Helper function to convert from SimdType enum to integer represenation (8/16/32). Returns 0 for generic simd.
type_to_simd(CompilerStats::Statistic::SimdType simd_type)428 int CompilerStats::Statistic::type_to_simd(CompilerStats::Statistic::SimdType simd_type)
429 {
430     switch (simd_type)
431     {
432         case SIMD_8:        return 8;
433         case SIMD_16:       return 16;
434         case SIMD_32:       return 32;
435         case SIMD_GENERIC:  return 0;
436         default:            return 0;
437     }
438 }
439 
440 // Helper function to convert from integer represenation (8/16/32) to SimdType enum. Returns SIMD_GENERIC for unexpected values.
simd_to_type(int simd)441 CompilerStats::Statistic::SimdType CompilerStats::Statistic::simd_to_type(int simd)
442 {
443     switch (simd)
444     {
445         case 8:     return SIMD_8;
446         case 16:    return SIMD_16;
447         case 32:    return SIMD_32;
448         case 0:     return SIMD_GENERIC;
449         default:    return SIMD_GENERIC;
450     }
451 }
452 
453 // Get stat value for simd size expressed as integer. Pass 0 to access generic.
454 CompilerStats::Statistic::Value& CompilerStats::Statistic::operator[](int simd)
455 {
456     const SimdType simd_type = simd_to_type(simd);
457     return values[simd_type];
458 }
459 
460 // Get stat value for simd size expressed as integer. Pass 0 to access generic.
461 const CompilerStats::Statistic::Value& CompilerStats::Statistic::operator[](int simd) const
462 {
463     const SimdType simd_type = simd_to_type(simd);
464     return values[simd_type];
465 }
466 
467