fix compile
[c11concurrency-benchmarks.git] / silo / scopedperf.hh
1 /*
2  * Canonical location:
3  *   git+ssh://amsterdam.csail.mit.edu/home/am1/prof/proftools.git
4  *   under spmc/lib/scopedperf.hh
5  *
6  * Modified by stephentu to disable for non C++11 builds
7  */
8
9 #ifndef _SCOPED_PERF_H_
10 #define _SCOPED_PERF_H_
11
12 #ifdef USE_PERF_CTRS
13
14 #if !defined(XV6)
15 #include <iostream>
16 #include <iomanip>
17 #include <sstream>
18 #include <assert.h>
19 #include <string.h>
20 #include <stdint.h>
21 #include <sys/time.h>
22 #endif
23
24 namespace scopedperf {
25
26 #if defined(XV6)
27 typedef u32 uint;
28 typedef u64 uint64_t;
29 #endif
30
31 /*
32  * statically enable/disable most of the generated code for profiling.
33  */
34 class default_enabler {
35  public:
36   bool enabled() const { return true; }
37 };
38
39 class always_enabled {
40  public:
41   bool enabled() const { return true; }
42 };
43
44 class always_disabled {
45  public:
46   bool enabled() const { return false; }
47 };
48
49 /*
50  * get CPU id function type
51  */
52 typedef int(*getcpu_fn)(void);
53
54 /*
55  * spinlock: mostly to avoid pthread mutex sleeping.
56  */
57 #if !defined(XV6_KERNEL)
58 class spinlock {
59  public:
60   spinlock() : x(0) {}
61
62   void acquire() {
63     while (!__sync_bool_compare_and_swap(&x, 0, 1))
64       ;
65   }
66
67   void release() {
68     x = 0;
69   }
70
71  private:
72   volatile uint x;
73 };
74 #endif
75
76 #if defined(XV6_KERNEL)
77 using ::spinlock;
78
79 static inline int sched_getcpu() {
80   return mycpu()->id;
81 }
82 #endif
83
84 class scoped_spinlock {
85  public:
86   scoped_spinlock(spinlock *larg) : l(larg) {
87     l->acquire();
88     held = true;
89   }
90
91   void release() {
92     if (held)
93       l->release();
94     held = false;
95   }
96
97   ~scoped_spinlock() { release(); }
98
99  private:
100   spinlock *const l;
101   bool held;
102 };
103
104
105 /*
106  * vector & pair: for portability.
107  */
108 template<class A, class B>
109 struct pair {
110   A first;
111   B second;
112 };
113
114 template<class A, class B>
115 pair<A, B>
116 make_pair(const A &a, const B &b)
117 {
118   pair<A, B> p;
119   p.first = a;
120   p.second = b;
121   return p;
122 }
123
124 template<class T>
125 struct vector {
126   T _buf[128];
127   uint _cnt;
128
129   vector() : _cnt(0) {}
130   void insert_front(T e) {
131     assert(_cnt < sizeof(_buf) / sizeof(T));
132     memmove(&_buf[1], &_buf[0], _cnt * sizeof(T));
133     _buf[0] = e;
134     _cnt++;
135   }
136   void push_back(T e) {
137     assert(_cnt < sizeof(_buf) / sizeof(T));
138     _buf[_cnt] = e;
139     _cnt++;
140   }
141 };
142
143 template<class T>
144 struct viter {
145   const vector<T> *_v;
146   int _pos;
147
148   viter(const vector<T> *v, int pos) : _v(v), _pos(pos) {}
149   bool operator!=(const viter &other) const { return _pos != other._pos; }
150   void operator++() { _pos++; }
151   T operator*() { return _v->_buf[_pos]; }
152 };
153
154 template<class T>
155 viter<T>
156 begin(const vector<T> &v)
157 {
158   return viter<T>(&v, 0);
159 }
160
161 template<class T>
162 viter<T>
163 end(const vector<T> &v)
164 {
165   return viter<T>(&v, v._cnt);
166 }
167
168
169 /*
170  * fast log-base-2, for histograms.
171  */
172 static const uint8_t log2table[256] = {
173 #define R2(x)   x,      x
174 #define R4(x)   R2(x),  R2(x)
175 #define R8(x)   R4(x),  R4(x)
176 #define R16(x)  R8(x),  R8(x)
177 #define R32(x)  R16(x), R16(x)
178 #define R64(x)  R32(x), R32(x)
179 #define R128(x) R64(x), R64(x)
180   0, 1, R2(2), R4(3), R8(4), R16(5), R32(6), R64(7), R128(8)
181 #undef R2
182 #undef R4
183 #undef R8
184 #undef R16
185 #undef R32
186 #undef R64
187 #undef R128
188 };
189
190 template<class T, int Nbits>
191 inline uintptr_t
192 log2r(T v)
193 {
194   if (Nbits == 8) {
195     return log2table[v];
196   } else {
197     T hi = v >> (Nbits/2);
198     if (hi)
199       return Nbits/2 + log2r<T, Nbits/2>(hi);
200     else
201       return log2r<T, Nbits/2>(v);
202   }
203 }
204
205 template<class T>
206 uintptr_t
207 log2(T v)
208 {
209   return log2r<T, sizeof(T)*8>(v);
210 }
211
212
213 /*
214  * ctrgroup: a group of performance counters.
215  */
216
217 template<typename... Counters>
218 class ctrgroup_chain;
219
220 template<>
221 class ctrgroup_chain<> {
222  public:
223   ctrgroup_chain() {}
224   static const uint cg_nctr = 0;
225   void cg_get_samples(uint64_t *v) const {}
226   void cg_get_delta(uint64_t *delta, uint64_t *prev) const {}
227   vector<const char*> get_names() const { return {}; }
228 };
229
230 template<typename One, typename... Others>
231 class ctrgroup_chain<One, Others...> : ctrgroup_chain<Others...> {
232  public:
233   ctrgroup_chain(One *x, Others*... y)
234     : ctrgroup_chain<Others...>(y...), ctr(x)
235   {
236     x->setup();
237   }
238
239   static const uint cg_nctr = 1 + ctrgroup_chain<Others...>::cg_nctr;
240
241   void cg_get_samples(uint64_t *v) const {
242     v[0] = ctr->sample();
243     ctrgroup_chain<Others...>::cg_get_samples(v+1);
244   }
245
246   void cg_get_delta(uint64_t *delta, uint64_t *prev) const {
247     uint64_t x = ctr->sample();
248     *delta = (x - *prev) & ctr->mask;
249     *prev = x;
250     ctrgroup_chain<Others...>::cg_get_delta(delta+1, prev+1);
251   }
252
253   vector<const char*> get_names() const {
254     vector<const char*> v = ctrgroup_chain<Others...>::get_names();
255     v.insert_front(ctr->name);
256     return v;
257   }
258
259  private:
260   const One *const ctr;
261 };
262
263 template<typename... Counters>
264 ctrgroup_chain<Counters...>
265 ctrgroup(Counters*... args)
266 {
267   return ctrgroup_chain<Counters...>(args...);
268 }
269
270
271 /*
272  * perfsum: aggregating counter deltas across multiple CPUs.
273  */
274 class perfsum_base {
275  public:
276   enum display_opt { show, hide };
277
278   perfsum_base(const char *n, display_opt d) : name(n), disp(d) {
279     scoped_spinlock x(get_sums_lock());
280     get_sums()->push_back(this);
281   }
282
283   static void printall(int w0 = 17, int w = 13) {
284     scoped_spinlock x(get_sums_lock());
285     auto sums = get_sums();
286     for (perfsum_base *ps: *sums)
287       if (ps->disp == show)
288         ps->print(w0, w);
289   }
290
291   static void resetall() {
292     scoped_spinlock x(get_sums_lock());
293     for (perfsum_base *ps: *get_sums())
294       ps->reset();
295   }
296
297   virtual void print(int w0, int w) const = 0;
298   virtual void reset() = 0;
299
300  protected:
301   template<class Row, class Callback>
302   static void print_row(const char *rowname, const Row &r,
303                         int w0, int w, Callback f)
304   {
305     std::cout << std::left << std::setw(w0) << rowname;
306     for (const auto &elem: r)
307       std::cout << std::left << std::setw(w) << f(elem) << " ";
308     std::cout << std::endl;
309   }
310
311   const char *name;
312   const display_opt disp;
313
314  private:
315   static vector<perfsum_base*> *get_sums() {
316     static vector<perfsum_base*> v;
317     return &v;
318   }
319
320   static spinlock *get_sums_lock() {
321     static spinlock l;
322     return &l;
323   }
324 };
325
326 static inline void
327 compiler_barrier()
328 {
329   /* Avoid compile-time reordering across performance counter reads */
330   __asm __volatile("" ::: "memory");
331 }
332
333 template<typename Enabler, typename... Counters>
334 class perfsum_tmpl : public perfsum_base, public Enabler {
335  public:
336   perfsum_tmpl(const ctrgroup_chain<Counters...> *c,
337                const char *n, perfsum_base::display_opt d)
338     : perfsum_base(n, d), cg(c)
339   {
340   }
341
342   static const uint ps_nctr = ctrgroup_chain<Counters...>::cg_nctr;
343
344  protected:
345   const struct ctrgroup_chain<Counters...> *const cg;
346   enum { maxcpu = 256 };
347
348   template<class Stats, class T>
349   static uint64_t addcpus(const Stats stat[], T f) {
350     uint64_t tot = 0;
351     for (uint i = 0; i < maxcpu; i++)
352       tot += f(&stat[i]);
353     return tot;
354   }
355 };
356
357 /*
358  * perfsum_ctr: aggregate counts of performance events.
359  */
360 template<typename Enabler, typename... Counters>
361 class perfsum_ctr : public perfsum_tmpl<Enabler, Counters...> {
362  public:
363   perfsum_ctr(const ctrgroup_chain<Counters...> *c,
364               const char *n, perfsum_base::display_opt d)
365     : perfsum_tmpl<Enabler, Counters...>(c, n, d), base(0)
366   {
367     reset();
368   }
369
370   perfsum_ctr(const char *n,
371               const perfsum_ctr<Enabler, Counters...> *basesum,
372               perfsum_base::display_opt d)
373     : perfsum_tmpl<Enabler, Counters...>(basesum->cg, n, d), base(basesum)
374   {
375     reset();
376   }
377
378   void get_samples(uint64_t *s) const {
379     compiler_barrier();
380     perfsum_tmpl<Enabler, Counters...>::cg->cg_get_samples(s);
381     compiler_barrier();
382   }
383
384   void record(uint cpuid, uint64_t *s) {
385     uint64_t delta[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
386
387     compiler_barrier();
388     perfsum_tmpl<Enabler, Counters...>::cg->cg_get_delta(delta, s);
389     compiler_barrier();
390
391     for (uint i = 0; i < perfsum_tmpl<Enabler, Counters...>::ps_nctr; i++)
392       stat[cpuid].sum[i] += delta[i];
393     stat[cpuid].count++;
394   }
395
396   void print(int w0, int w) const /* override */ {
397     if (!Enabler::enabled())
398       return;
399
400     auto &cg = perfsum_tmpl<Enabler, Counters...>::cg;
401     vector<pair<uint64_t, uint64_t> > p;
402     for (uint i = 0; i < cg->cg_nctr; i++) {
403       uint64_t b =
404         base ? this->addcpus(base->stat, [&](const stats *s) { return s->sum[i]; })
405              : this->addcpus(stat,       [&](const stats *s) { return s->count; });
406       p.push_back(make_pair(b,
407         this->addcpus(stat, [i](const stats *s) { return s->sum[i]; })));
408     }
409
410     this->print_row(perfsum_base::name, cg->get_names(), w0, w, [](const char *name)
411               { return name; });
412     this->print_row("  avg",   p, w0, w, [](const pair<uint64_t, uint64_t> &e)
413 #if !defined(XV6)
414               { return ((double) e.second) / (double) e.first; }
415 #else
416               { return e.second / e.first; }
417 #endif
418               );
419     this->print_row("  total", p, w0, w, [](const pair<uint64_t, uint64_t> &e)
420               { return e.second; });
421     this->print_row("  count", p, w0, w, [](const pair<uint64_t, uint64_t> &e)
422               { return e.first; });
423   }
424
425   void reset() /* override */ {
426     memset(stat, 0, sizeof(stat));
427   }
428
429  private:
430   struct stats {
431     uint64_t count;
432     uint64_t sum[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
433   } __attribute__((aligned (64)));
434
435   struct stats stat[perfsum_tmpl<Enabler, Counters...>::maxcpu];
436   const struct perfsum_ctr<Enabler, Counters...> *const base;
437 };
438
439 template<typename Enabler, typename... Counters>
440 class perfsum_ctr_inlinegroup :
441   public ctrgroup_chain<Counters...>,
442   public perfsum_ctr<Enabler, Counters...>
443 {
444  public:
445   perfsum_ctr_inlinegroup(const char *n, perfsum_base::display_opt d,
446                           Counters*... ctrs)
447     : ctrgroup_chain<Counters...>(ctrs...),
448       perfsum_ctr<Enabler, Counters...>(this, n, d) {}
449 };
450
451 template<typename Enabler = default_enabler, typename... Counters>
452 perfsum_ctr<Enabler, Counters...>
453 perfsum(const char *name, const ctrgroup_chain<Counters...> *c,
454         const perfsum_base::display_opt d = perfsum_base::show)
455 {
456   return perfsum_ctr<Enabler, Counters...>(c, name, d);
457 }
458
459 template<typename Enabler = default_enabler, typename... Counters>
460 perfsum_ctr_inlinegroup<Enabler, Counters...>
461 perfsum_group(const char *name, Counters*... c)
462 {
463   return perfsum_ctr_inlinegroup<Enabler, Counters...>(name, perfsum_base::show, c...);
464 }
465
466 template<typename Enabler, typename... Counters>
467 perfsum_ctr<Enabler, Counters...>
468 perfsum_frac(const char *name,
469              const perfsum_ctr<Enabler, Counters...> *base)
470 {
471   return perfsum_ctr<Enabler, Counters...>(name, base, perfsum_base::show);
472 }
473
474 /*
475  * perfsum_hist: histogram-based aggregates.
476  */
477 template<typename Enabler, typename... Counters>
478 class perfsum_hist_tmpl : public perfsum_tmpl<Enabler, Counters...> {
479  public:
480   perfsum_hist_tmpl(const ctrgroup_chain<Counters...> *c,
481                     const char *n, perfsum_base::display_opt d)
482     : perfsum_tmpl<Enabler, Counters...>(c, n, d)
483   {
484     reset();
485   }
486
487   void get_samples(uint64_t *s) const {
488     compiler_barrier();
489     perfsum_tmpl<Enabler, Counters...>::cg->cg_get_samples(s);
490     compiler_barrier();
491   }
492
493   void record(uint cpuid, uint64_t *s) {
494     uint64_t delta[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
495
496     compiler_barrier();
497     perfsum_tmpl<Enabler, Counters...>::cg->cg_get_delta(delta, s);
498     compiler_barrier();
499
500     for (uint i = 0; i < perfsum_tmpl<Enabler, Counters...>::ps_nctr; i++)
501       stat[cpuid].hist[i].count[log2(delta[i])]++;
502   }
503
504   void print(int w0, int w) const /* override */ {
505     if (!Enabler::enabled())
506       return;
507
508     uint first = nbuckets, last = 0;
509
510     auto &cg = perfsum_tmpl<Enabler, Counters...>::cg;
511     vector<buckets> p;
512     for (uint i = 0; i < cg->cg_nctr; i++) {
513       buckets v;
514       for (uint j = 0; j < nbuckets; j++) {
515         v.count[j] = this->addcpus(stat, [&](const stats *s) { return s->hist[i].count[j]; });
516         if (v.count[j]) {
517           if (j < first) first = j;
518           if (j > last)  last = j;
519         }
520       }
521       p.push_back(v);
522     }
523
524     this->print_row(perfsum_base::name, cg->get_names(), w0, w, [](const char *name)
525               { return name; });
526     for (uint i = first; i <= last; i++) {
527       char n[64];
528       snprintf(n, sizeof(n), "  < 2^%d", i);
529       this->print_row(n, p, w0, w, [&](const buckets &b) { return b.count[i]; });
530     }
531     this->print_row("  total", p, w0, w, [](const buckets &b)
532                     { uint64_t s = 0; for (auto x: b.count) s += x; return s; });
533   }
534
535   void reset() /* override */ {
536     memset(stat, 0, sizeof(stat));
537   }
538
539  private:
540   enum { nbuckets = sizeof(uint64_t)*8 + 1 };
541
542   struct buckets {
543     uint64_t count[nbuckets];
544   };
545
546   struct stats {
547     struct buckets hist[perfsum_tmpl<Enabler, Counters...>::ps_nctr];
548   } __attribute__((aligned (64)));
549
550   struct stats stat[perfsum_tmpl<Enabler, Counters...>::maxcpu];
551 };
552
553 template<typename Enabler = default_enabler, typename... Counters>
554 perfsum_hist_tmpl<Enabler, Counters...>
555 perfsum_hist(const char *name, const ctrgroup_chain<Counters...> *c,
556              const perfsum_base::display_opt d = perfsum_base::show)
557 {
558   return perfsum_hist_tmpl<Enabler, Counters...>(c, name, d);
559 }
560
561
562 /*
563  * namedctr &c: actual counter implementations.
564  */
565 template<uint64_t CounterWidth>
566 class namedctr {
567  public:
568   namedctr(const char *n) : name(n) {}
569   void setup() {}
570   const char *name;
571   static const uint64_t mask =
572     ((1ULL << (CounterWidth - 1)) - 1) << 1 | 1;
573 };
574
575 class tsc_ctr : public namedctr<64> {
576  public:
577   tsc_ctr() : namedctr("tsc") {}
578   static uint64_t sample() {
579     uint64_t a, d;
580     __asm __volatile("rdtsc" : "=a" (a), "=d" (d));
581     return a | (d << 32);
582   }
583 };
584
585 class tscp_ctr : public namedctr<64> {
586  public:
587   tscp_ctr() : namedctr("tscp") {}
588   static uint64_t sample() {
589     uint64_t a, d, c;
590     __asm __volatile("rdtscp" : "=a" (a), "=d" (d), "=c" (c));
591     return a | (d << 32);
592   }
593 };
594
595 template<uint64_t CounterWidth>
596 class pmc_ctr : public namedctr<CounterWidth> {
597  public:
598   pmc_ctr(int n) : namedctr<CounterWidth>(mkname(n)), cn(n) {}
599   pmc_ctr(const char *nm) : namedctr<CounterWidth>(nm), cn(-1) {}
600
601   uint64_t sample() const {
602     uint64_t a, d;
603     __asm __volatile("rdpmc" : "=a" (a), "=d" (d) : "c" (cn));
604     return a | (d << 32);
605   }
606
607   int cn;
608
609  private:
610   static const char* mkname(int n) {
611     char *buf = new char[32];
612     snprintf(buf, 32, "pmc%d", n);
613     return buf;
614   }
615 };
616
617 template<uint64_t CounterWidth = 64>
618 class pmc_setup : public pmc_ctr<CounterWidth> {
619  public:
620   pmc_setup(uint64_t v, const char *nm)
621     : pmc_ctr<CounterWidth>(nm), pmc_v(v) {}
622
623   void setup() {
624     if (pmc_ctr<CounterWidth>::cn >= 0)
625       return;
626
627     /*
628      * XXX detect how many counters the hardware has
629      */
630     static bool pmcuse[4];
631     static spinlock pmcuselock;
632
633     int n = 0;
634     scoped_spinlock x(&pmcuselock);
635     while (n < 4 && pmcuse[n])
636       n++;
637     assert(n < 4);
638     pmcuse[n] = true;
639     x.release();
640
641 #if !defined(XV6)
642     // ugly but effective
643     std::stringstream ss;
644     ss << "for f in /sys/kernel/spmc/cpu*/" << n << "; do "
645        << "echo " << std::hex << pmc_v << " > $f; done";
646     assert(0 == system(ss.str().c_str()));
647 #endif
648
649     pmc_ctr<CounterWidth>::cn = n;
650   }
651
652  private:
653   uint64_t pmc_v;
654 };
655
656 #if !defined(XV6)
657 class tod_ctr : public namedctr<64> {
658  public:
659   tod_ctr() : namedctr("tod-usec") {}
660   uint64_t sample() const {
661     struct timeval tv;
662     gettimeofday(&tv, 0);
663     return ((uint64_t) tv.tv_usec) + ((uint64_t) tv.tv_sec) * 1000000;
664   }
665 };
666 #endif
667
668 class zero_ctr : public namedctr<64> {
669  public:
670   zero_ctr() : namedctr("zero") {}
671   uint64_t sample() const { return 0; }
672 };
673
674
675 /*
676  * scoped performance-counting regions, which record samples into a perfsum.
677  */
678 template<typename Perfsum>
679 class base_perf_region {
680  public:
681   base_perf_region(Perfsum *psarg, getcpu_fn getcpu)
682     : ps(psarg), enabled(ps->enabled()), cpuid(enabled ? getcpu() : 0)
683   {
684     if (enabled)
685       ps->get_samples(s);
686   }
687
688   // invoke lap multiple times to precisely measure iterations
689   // (use same measurement for end of one & start of next round)
690   void lap() {
691     if (enabled)
692       ps->record(cpuid, s);
693   }
694
695  private:
696   Perfsum *const ps;
697   const bool enabled;
698   const uint cpuid;
699   uint64_t s[Perfsum::ps_nctr];
700 };
701
702 template<typename Perfsum>
703 class scoped_perf_region : public base_perf_region<Perfsum> {
704  public:
705   scoped_perf_region(Perfsum *psarg, getcpu_fn getcpu)
706     : base_perf_region<Perfsum>(psarg, getcpu) {}
707   ~scoped_perf_region() { base_perf_region<Perfsum>::lap(); }
708 };
709
710 template<typename Perfsum>
711 class killable_perf_region : public base_perf_region<Perfsum> {
712  public:
713   killable_perf_region(Perfsum *psarg, getcpu_fn getcpu)
714     : base_perf_region<Perfsum>(psarg, getcpu), active(true) {}
715   ~killable_perf_region() { stop(); }
716
717   // perform a final measurement, if needed before destructor
718   void stop() {
719     if (active)
720       base_perf_region<Perfsum>::lap();
721     active = false;
722   }
723
724   // prevent destructor from performing a measurement
725   void kill() { active = false; }
726
727  private:
728   bool active;
729 };
730
731 template<typename Perfsum>
732 scoped_perf_region<Perfsum>
733 perf_region(Perfsum *ps, getcpu_fn getcpu = sched_getcpu)
734 {
735   return scoped_perf_region<Perfsum>(ps, getcpu);
736 }
737
738 template<typename Perfsum>
739 killable_perf_region<Perfsum>
740 killable_region(Perfsum *ps, getcpu_fn getcpu = sched_getcpu)
741 {
742   return killable_perf_region<Perfsum>(ps, getcpu);
743 }
744
745
746 /*
747  * macros for the common case of putting in a scoped perf-counting region.
748  */
749 #define __PERF_CONCAT2(a, b)  a ## b
750 #define __PERF_CONCAT(a, b)   __PERF_CONCAT2(a, b)
751 #define __PERF_ANON           __PERF_CONCAT(__anon_id_, __COUNTER__)
752
753 #define __PERF_REGION(region_var, sum_var, region_type, text, group)           \
754   static auto __PERF_CONCAT(sum_var, _sum) = scopedperf::perfsum(text, group); \
755   auto region_var = region_type(&__PERF_CONCAT(sum_var, _sum));
756
757 #define ANON_REGION(text, group) \
758   __PERF_REGION(__PERF_ANON, __PERF_ANON, scopedperf::perf_region, text, group)
759 #define PERF_REGION(var, text, group) \
760   __PERF_REGION(var, __PERF_ANON, scopedperf::perf_region, text, group)
761 #define KILLABLE_REGION(var, text, group) \
762   __PERF_REGION(var, __PERF_ANON, scopedperf::killable_region, text, group)
763
764 #define STATIC_COUNTER_DECL(ctrtype, ctrname, groupname) \
765   static ctrtype ctrname; \
766   static ::scopedperf::ctrgroup_chain< ctrtype > groupname(&ctrname);
767 #define PERF_EXPR(expr) expr
768 #define PERF_DECL(decl) decl
769
770 #define CLASS_STATIC_COUNTER_DECL(ctrtype, ctrname, groupname) \
771   static ctrtype ctrname; \
772   static ::scopedperf::ctrgroup_chain< ctrtype > groupname;
773 #define CLASS_STATIC_COUNTER_IMPL(clsname, ctrtype, ctrname, groupname) \
774   ctrtype clsname::ctrname; \
775   ::scopedperf::ctrgroup_chain< ctrtype > clsname::groupname(&ctrname) ;
776
777 } /* namespace scopedperf */
778
779 #else /* !USE_PERF_CTRS */
780
781 #define ANON_REGION(text, group) ((void)0)
782 #define PERF_REGION(var, text, group) ((void)0)
783 #define KILLABLE_REGION(var, text, group) ((void)0)
784
785 #define STATIC_COUNTER_DECL(ctrtype, ctrname, groupname)
786 #define PERF_EXPR(expr) ((void)0)
787 #define PERF_DECL(decl)
788
789 #define CLASS_STATIC_COUNTER_DECL(ctrtype, ctrname, groupname)
790 #define CLASS_STATIC_COUNTER_IMPL(clsname, ctrtype, ctrname, groupname)
791
792 #endif /* USE_PERF_CTRS */
793
794 #endif /* _SCOPED_PERF_H_ */