1 | //===-- Benchmark function tests -----------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "LibcBenchmark.h" |
10 | #include "llvm/ADT/ArrayRef.h" |
11 | #include "llvm/ADT/SmallVector.h" |
12 | #include "gmock/gmock.h" |
13 | #include "gtest/gtest.h" |
14 | #include <chrono> |
15 | #include <limits> |
16 | #include <optional> |
17 | #include <queue> |
18 | #include <vector> |
19 | |
20 | using std::chrono::nanoseconds; |
21 | using ::testing::ElementsAre; |
22 | using ::testing::Field; |
23 | using ::testing::IsEmpty; |
24 | using ::testing::SizeIs; |
25 | |
26 | namespace llvm { |
27 | namespace libc_benchmarks { |
28 | namespace { |
29 | |
30 | // A simple parameter provider returning a zero initialized vector of size |
31 | // `iterations`. |
32 | struct DummyParameterProvider { |
33 | std::vector<char> generateBatch(size_t iterations) { |
34 | return std::vector<char>(iterations); |
35 | } |
36 | }; |
37 | |
38 | class LibcBenchmark : public ::testing::Test { |
39 | public: |
40 | // A Clock interface suitable for testing. |
41 | // - Either it returns 0, |
42 | // - Or a timepoint coming from the `setMeasurements` call. |
43 | Duration now() { |
44 | if (!MaybeTimepoints) |
45 | return {}; |
46 | assert(!MaybeTimepoints->empty()); |
47 | const Duration timepoint = MaybeTimepoints->front(); |
48 | MaybeTimepoints->pop(); |
49 | return timepoint; |
50 | } |
51 | |
52 | protected: |
53 | void SetUp() override { Options.Log = BenchmarkLog::Full; } |
54 | |
55 | void TearDown() override { |
56 | // We make sure all the expected measurements were performed. |
57 | if (MaybeTimepoints) |
58 | EXPECT_THAT(*MaybeTimepoints, IsEmpty()); |
59 | } |
60 | |
61 | BenchmarkResult run() { |
62 | return benchmark(Options, PP&: ParameterProvider, foo: DummyFunction, Clock&: *this); |
63 | } |
64 | |
65 | void setMeasurements(llvm::ArrayRef<Duration> Durations) { |
66 | MaybeTimepoints.emplace(); // Create the optional value. |
67 | Duration CurrentTime = nanoseconds(1); |
68 | for (const auto &Duration : Durations) { |
69 | MaybeTimepoints->push(CurrentTime); |
70 | CurrentTime += Duration; |
71 | MaybeTimepoints->push(CurrentTime); |
72 | CurrentTime += nanoseconds(1); |
73 | } |
74 | } |
75 | |
76 | BenchmarkOptions Options; |
77 | |
78 | private: |
79 | DummyParameterProvider ParameterProvider; |
80 | static char DummyFunction(char Payload) { return Payload; } |
81 | std::optional<std::queue<Duration>> MaybeTimepoints; |
82 | }; |
83 | |
84 | TEST_F(LibcBenchmark, MaxSamplesReached) { |
85 | Options.MaxSamples = 1; |
86 | const auto Result = run(); |
87 | EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 1); |
88 | EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::MaxSamplesReached); |
89 | } |
90 | |
91 | TEST_F(LibcBenchmark, MaxDurationReached) { |
92 | Options.MaxDuration = nanoseconds(10); |
93 | setMeasurements({nanoseconds(11)}); |
94 | const auto Result = run(); |
95 | EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 1); |
96 | EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::MaxDurationReached); |
97 | } |
98 | |
99 | TEST_F(LibcBenchmark, MaxIterationsReached) { |
100 | Options.InitialIterations = 1; |
101 | Options.MaxIterations = 20; |
102 | Options.ScalingFactor = 2; |
103 | Options.Epsilon = 0; // unreachable. |
104 | const auto Result = run(); |
105 | EXPECT_THAT(*Result.MaybeBenchmarkLog, |
106 | ElementsAre(Field(&BenchmarkState::LastSampleIterations, 1), |
107 | Field(&BenchmarkState::LastSampleIterations, 2), |
108 | Field(&BenchmarkState::LastSampleIterations, 4), |
109 | Field(&BenchmarkState::LastSampleIterations, 8), |
110 | Field(&BenchmarkState::LastSampleIterations, 16), |
111 | Field(&BenchmarkState::LastSampleIterations, 32))); |
112 | EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 6); |
113 | EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::MaxIterationsReached); |
114 | } |
115 | |
116 | TEST_F(LibcBenchmark, MinSamples) { |
117 | Options.MinSamples = 4; |
118 | Options.ScalingFactor = 2; |
119 | Options.Epsilon = std::numeric_limits<double>::max(); // always reachable. |
120 | setMeasurements( |
121 | {nanoseconds(1), nanoseconds(2), nanoseconds(4), nanoseconds(8)}); |
122 | const auto Result = run(); |
123 | EXPECT_THAT(*Result.MaybeBenchmarkLog, |
124 | ElementsAre(Field(&BenchmarkState::LastSampleIterations, 1), |
125 | Field(&BenchmarkState::LastSampleIterations, 2), |
126 | Field(&BenchmarkState::LastSampleIterations, 4), |
127 | Field(&BenchmarkState::LastSampleIterations, 8))); |
128 | EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 4); |
129 | EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::PrecisionReached); |
130 | } |
131 | |
132 | TEST_F(LibcBenchmark, Epsilon) { |
133 | Options.MinSamples = 4; |
134 | Options.ScalingFactor = 2; |
135 | Options.Epsilon = std::numeric_limits<double>::max(); // always reachable. |
136 | setMeasurements( |
137 | {nanoseconds(1), nanoseconds(2), nanoseconds(4), nanoseconds(8)}); |
138 | const auto Result = run(); |
139 | EXPECT_THAT(*Result.MaybeBenchmarkLog, |
140 | ElementsAre(Field(&BenchmarkState::LastSampleIterations, 1), |
141 | Field(&BenchmarkState::LastSampleIterations, 2), |
142 | Field(&BenchmarkState::LastSampleIterations, 4), |
143 | Field(&BenchmarkState::LastSampleIterations, 8))); |
144 | EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 4); |
145 | EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::PrecisionReached); |
146 | } |
147 | |
148 | TEST(ArrayRefLoop, Cycle) { |
149 | std::array<int, 2> array = {1, 2}; |
150 | EXPECT_THAT(cycle(array, 0), ElementsAre()); |
151 | EXPECT_THAT(cycle(array, 1), ElementsAre(1)); |
152 | EXPECT_THAT(cycle(array, 2), ElementsAre(1, 2)); |
153 | EXPECT_THAT(cycle(array, 3), ElementsAre(1, 2, 1)); |
154 | EXPECT_THAT(cycle(array, 4), ElementsAre(1, 2, 1, 2)); |
155 | EXPECT_THAT(cycle(array, 5), ElementsAre(1, 2, 1, 2, 1)); |
156 | } |
157 | |
158 | TEST(ByteConstrainedArray, Simple) { |
159 | EXPECT_THAT((ByteConstrainedArray<char, 17>()), SizeIs(17)); |
160 | EXPECT_THAT((ByteConstrainedArray<uint16_t, 17>()), SizeIs(8)); |
161 | EXPECT_THAT((ByteConstrainedArray<uint32_t, 17>()), SizeIs(4)); |
162 | EXPECT_THAT((ByteConstrainedArray<uint64_t, 17>()), SizeIs(2)); |
163 | |
164 | EXPECT_LE(sizeof(ByteConstrainedArray<char, 17>), 17U); |
165 | EXPECT_LE(sizeof(ByteConstrainedArray<uint16_t, 17>), 17U); |
166 | EXPECT_LE(sizeof(ByteConstrainedArray<uint32_t, 17>), 17U); |
167 | EXPECT_LE(sizeof(ByteConstrainedArray<uint64_t, 17>), 17U); |
168 | } |
169 | |
170 | TEST(ByteConstrainedArray, Cycle) { |
171 | ByteConstrainedArray<uint64_t, 17> TwoValues{._M_elems: {1UL, 2UL}}; |
172 | EXPECT_THAT(cycle(TwoValues, 5), ElementsAre(1, 2, 1, 2, 1)); |
173 | } |
174 | } // namespace |
175 | } // namespace libc_benchmarks |
176 | } // namespace llvm |
177 | |