//===-- Benchmark function tests -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "LibcBenchmark.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include using std::chrono::nanoseconds; using ::testing::ElementsAre; using ::testing::Field; using ::testing::IsEmpty; using ::testing::SizeIs; namespace llvm { namespace libc_benchmarks { namespace { // A simple parameter provider returning a zero initialized vector of size // `iterations`. struct DummyParameterProvider { std::vector generateBatch(size_t iterations) { return std::vector(iterations); } }; class LibcBenchmark : public ::testing::Test { public: // A Clock interface suitable for testing. // - Either it returns 0, // - Or a timepoint coming from the `setMeasurements` call. Duration now() { if (!MaybeTimepoints) return {}; assert(!MaybeTimepoints->empty()); const Duration timepoint = MaybeTimepoints->front(); MaybeTimepoints->pop(); return timepoint; } protected: void SetUp() override { Options.Log = BenchmarkLog::Full; } void TearDown() override { // We make sure all the expected measurements were performed. if (MaybeTimepoints) EXPECT_THAT(*MaybeTimepoints, IsEmpty()); } BenchmarkResult run() { return benchmark(Options, ParameterProvider, DummyFunction, *this); } void setMeasurements(llvm::ArrayRef Durations) { MaybeTimepoints.emplace(); // Create the optional value. Duration CurrentTime = nanoseconds(1); for (const auto &Duration : Durations) { MaybeTimepoints->push(CurrentTime); CurrentTime += Duration; MaybeTimepoints->push(CurrentTime); CurrentTime += nanoseconds(1); } } BenchmarkOptions Options; private: DummyParameterProvider ParameterProvider; static char DummyFunction(char Payload) { return Payload; } llvm::Optional> MaybeTimepoints; }; TEST_F(LibcBenchmark, MaxSamplesReached) { Options.MaxSamples = 1; const auto Result = run(); EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 1); EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::MaxSamplesReached); } TEST_F(LibcBenchmark, MaxDurationReached) { Options.MaxDuration = nanoseconds(10); setMeasurements({nanoseconds(11)}); const auto Result = run(); EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 1); EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::MaxDurationReached); } TEST_F(LibcBenchmark, MaxIterationsReached) { Options.InitialIterations = 1; Options.MaxIterations = 20; Options.ScalingFactor = 2; Options.Epsilon = 0; // unreachable. const auto Result = run(); EXPECT_THAT(*Result.MaybeBenchmarkLog, ElementsAre(Field(&BenchmarkState::LastSampleIterations, 1), Field(&BenchmarkState::LastSampleIterations, 2), Field(&BenchmarkState::LastSampleIterations, 4), Field(&BenchmarkState::LastSampleIterations, 8), Field(&BenchmarkState::LastSampleIterations, 16), Field(&BenchmarkState::LastSampleIterations, 32))); EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 6); EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::MaxIterationsReached); } TEST_F(LibcBenchmark, MinSamples) { Options.MinSamples = 4; Options.ScalingFactor = 2; Options.Epsilon = std::numeric_limits::max(); // always reachable. setMeasurements( {nanoseconds(1), nanoseconds(2), nanoseconds(4), nanoseconds(8)}); const auto Result = run(); EXPECT_THAT(*Result.MaybeBenchmarkLog, ElementsAre(Field(&BenchmarkState::LastSampleIterations, 1), Field(&BenchmarkState::LastSampleIterations, 2), Field(&BenchmarkState::LastSampleIterations, 4), Field(&BenchmarkState::LastSampleIterations, 8))); EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 4); EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::PrecisionReached); } TEST_F(LibcBenchmark, Epsilon) { Options.MinSamples = 4; Options.ScalingFactor = 2; Options.Epsilon = std::numeric_limits::max(); // always reachable. setMeasurements( {nanoseconds(1), nanoseconds(2), nanoseconds(4), nanoseconds(8)}); const auto Result = run(); EXPECT_THAT(*Result.MaybeBenchmarkLog, ElementsAre(Field(&BenchmarkState::LastSampleIterations, 1), Field(&BenchmarkState::LastSampleIterations, 2), Field(&BenchmarkState::LastSampleIterations, 4), Field(&BenchmarkState::LastSampleIterations, 8))); EXPECT_THAT(Result.MaybeBenchmarkLog->size(), 4); EXPECT_THAT(Result.TerminationStatus, BenchmarkStatus::PrecisionReached); } TEST(ArrayRefLoop, Cycle) { std::array array = {1, 2}; EXPECT_THAT(cycle(array, 0), ElementsAre()); EXPECT_THAT(cycle(array, 1), ElementsAre(1)); EXPECT_THAT(cycle(array, 2), ElementsAre(1, 2)); EXPECT_THAT(cycle(array, 3), ElementsAre(1, 2, 1)); EXPECT_THAT(cycle(array, 4), ElementsAre(1, 2, 1, 2)); EXPECT_THAT(cycle(array, 5), ElementsAre(1, 2, 1, 2, 1)); } TEST(ByteConstrainedArray, Simple) { EXPECT_THAT((ByteConstrainedArray()), SizeIs(17)); EXPECT_THAT((ByteConstrainedArray()), SizeIs(8)); EXPECT_THAT((ByteConstrainedArray()), SizeIs(4)); EXPECT_THAT((ByteConstrainedArray()), SizeIs(2)); EXPECT_LE(sizeof(ByteConstrainedArray), 17U); EXPECT_LE(sizeof(ByteConstrainedArray), 17U); EXPECT_LE(sizeof(ByteConstrainedArray), 17U); EXPECT_LE(sizeof(ByteConstrainedArray), 17U); } TEST(ByteConstrainedArray, Cycle) { ByteConstrainedArray TwoValues{{1UL, 2UL}}; EXPECT_THAT(cycle(TwoValues, 5), ElementsAre(1, 2, 1, 2, 1)); } } // namespace } // namespace libc_benchmarks } // namespace llvm