• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <thread>
2 
3 #include "../src/perf_counters.h"
4 #include "gtest/gtest.h"
5 
6 #ifndef GTEST_SKIP
7 struct MsgHandler {
operator =MsgHandler8   void operator=(std::ostream&) {}
9 };
10 #define GTEST_SKIP() return MsgHandler() = std::cout
11 #endif
12 
13 using benchmark::internal::PerfCounters;
14 using benchmark::internal::PerfCounterValues;
15 
16 namespace {
17 const char kGenericPerfEvent1[] = "CYCLES";
18 const char kGenericPerfEvent2[] = "BRANCHES";
19 const char kGenericPerfEvent3[] = "INSTRUCTIONS";
20 
TEST(PerfCountersTest,Init)21 TEST(PerfCountersTest, Init) {
22   EXPECT_EQ(PerfCounters::Initialize(), PerfCounters::kSupported);
23 }
24 
TEST(PerfCountersTest,OneCounter)25 TEST(PerfCountersTest, OneCounter) {
26   if (!PerfCounters::kSupported) {
27     GTEST_SKIP() << "Performance counters not supported.\n";
28   }
29   EXPECT_TRUE(PerfCounters::Initialize());
30   EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1}).IsValid());
31 }
32 
TEST(PerfCountersTest,NegativeTest)33 TEST(PerfCountersTest, NegativeTest) {
34   if (!PerfCounters::kSupported) {
35     EXPECT_FALSE(PerfCounters::Initialize());
36     return;
37   }
38   EXPECT_TRUE(PerfCounters::Initialize());
39   EXPECT_FALSE(PerfCounters::Create({}).IsValid());
40   EXPECT_FALSE(PerfCounters::Create({""}).IsValid());
41   EXPECT_FALSE(PerfCounters::Create({"not a counter name"}).IsValid());
42   {
43     EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
44                                       kGenericPerfEvent3})
45                     .IsValid());
46   }
47   EXPECT_FALSE(
48       PerfCounters::Create({kGenericPerfEvent2, "", kGenericPerfEvent1})
49           .IsValid());
50   EXPECT_FALSE(PerfCounters::Create({kGenericPerfEvent3, "not a counter name",
51                                      kGenericPerfEvent1})
52                    .IsValid());
53   {
54     EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
55                                       kGenericPerfEvent3})
56                     .IsValid());
57   }
58   EXPECT_FALSE(
59       PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
60                             kGenericPerfEvent3, "MISPREDICTED_BRANCH_RETIRED"})
61           .IsValid());
62 }
63 
TEST(PerfCountersTest,Read1Counter)64 TEST(PerfCountersTest, Read1Counter) {
65   if (!PerfCounters::kSupported) {
66     GTEST_SKIP() << "Test skipped because libpfm is not supported.\n";
67   }
68   EXPECT_TRUE(PerfCounters::Initialize());
69   auto counters = PerfCounters::Create({kGenericPerfEvent1});
70   EXPECT_TRUE(counters.IsValid());
71   PerfCounterValues values1(1);
72   EXPECT_TRUE(counters.Snapshot(&values1));
73   EXPECT_GT(values1[0], 0);
74   PerfCounterValues values2(1);
75   EXPECT_TRUE(counters.Snapshot(&values2));
76   EXPECT_GT(values2[0], 0);
77   EXPECT_GT(values2[0], values1[0]);
78 }
79 
TEST(PerfCountersTest,Read2Counters)80 TEST(PerfCountersTest, Read2Counters) {
81   if (!PerfCounters::kSupported) {
82     GTEST_SKIP() << "Test skipped because libpfm is not supported.\n";
83   }
84   EXPECT_TRUE(PerfCounters::Initialize());
85   auto counters =
86       PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2});
87   EXPECT_TRUE(counters.IsValid());
88   PerfCounterValues values1(2);
89   EXPECT_TRUE(counters.Snapshot(&values1));
90   EXPECT_GT(values1[0], 0);
91   EXPECT_GT(values1[1], 0);
92   PerfCounterValues values2(2);
93   EXPECT_TRUE(counters.Snapshot(&values2));
94   EXPECT_GT(values2[0], 0);
95   EXPECT_GT(values2[1], 0);
96 }
97 
do_work()98 size_t do_work() {
99   size_t res = 0;
100   for (size_t i = 0; i < 100000000; ++i) res += i * i;
101   return res;
102 }
103 
measure(size_t threadcount,PerfCounterValues * values1,PerfCounterValues * values2)104 void measure(size_t threadcount, PerfCounterValues* values1,
105              PerfCounterValues* values2) {
106   BM_CHECK_NE(values1, nullptr);
107   BM_CHECK_NE(values2, nullptr);
108   std::vector<std::thread> threads(threadcount);
109   auto work = [&]() { BM_CHECK(do_work() > 1000); };
110 
111   // We need to first set up the counters, then start the threads, so the
112   // threads would inherit the counters. But later, we need to first destroy the
113   // thread pool (so all the work finishes), then measure the counters. So the
114   // scopes overlap, and we need to explicitly control the scope of the
115   // threadpool.
116   auto counters =
117       PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent3});
118   for (auto& t : threads) t = std::thread(work);
119   counters.Snapshot(values1);
120   for (auto& t : threads) t.join();
121   counters.Snapshot(values2);
122 }
123 
TEST(PerfCountersTest,MultiThreaded)124 TEST(PerfCountersTest, MultiThreaded) {
125   if (!PerfCounters::kSupported) {
126     GTEST_SKIP() << "Test skipped because libpfm is not supported.";
127   }
128   EXPECT_TRUE(PerfCounters::Initialize());
129   PerfCounterValues values1(2);
130   PerfCounterValues values2(2);
131 
132   measure(2, &values1, &values2);
133   std::vector<double> D1{static_cast<double>(values2[0] - values1[0]),
134                          static_cast<double>(values2[1] - values1[1])};
135 
136   measure(4, &values1, &values2);
137   std::vector<double> D2{static_cast<double>(values2[0] - values1[0]),
138                          static_cast<double>(values2[1] - values1[1])};
139 
140   // Some extra work will happen on the main thread - like joining the threads
141   // - so the ratio won't be quite 2.0, but very close.
142   EXPECT_GE(D2[0], 1.9 * D1[0]);
143   EXPECT_GE(D2[1], 1.9 * D1[1]);
144 }
145 }  // namespace
146