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