xref: /aosp_15_r20/external/llvm-libc/test/src/math/performance_testing/SingleInputSingleOutputPerf.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1 //===-- Common utility class for differential analysis --------------------===//
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 "src/__support/CPP/algorithm.h"
10 #include "src/__support/FPUtil/FPBits.h"
11 #include "src/__support/macros/config.h"
12 #include "test/src/math/performance_testing/Timer.h"
13 
14 #include <fstream>
15 
16 namespace LIBC_NAMESPACE_DECL {
17 namespace testing {
18 
19 template <typename T> class SingleInputSingleOutputPerf {
20   using FPBits = fputil::FPBits<T>;
21   using StorageType = typename FPBits::StorageType;
22   static constexpr StorageType UIntMax =
23       cpp::numeric_limits<StorageType>::max();
24 
25 public:
26   typedef T Func(T);
27 
runPerfInRange(Func myFunc,Func otherFunc,StorageType startingBit,StorageType endingBit,size_t rounds,std::ofstream & log)28   static void runPerfInRange(Func myFunc, Func otherFunc,
29                              StorageType startingBit, StorageType endingBit,
30                              size_t rounds, std::ofstream &log) {
31     size_t n = 10'010'001;
32     if (sizeof(StorageType) <= sizeof(size_t))
33       n = cpp::min(n, static_cast<size_t>(endingBit - startingBit));
34 
35     auto runner = [=](Func func) {
36       StorageType step = (endingBit - startingBit) / n;
37       if (step == 0)
38         step = 1;
39       [[maybe_unused]] volatile T result;
40       for (size_t i = 0; i < rounds; i++) {
41         for (StorageType bits = startingBit; bits < endingBit; bits += step) {
42           T x = FPBits(bits).get_val();
43           result = func(x);
44         }
45       }
46     };
47 
48     Timer timer;
49     timer.start();
50     runner(myFunc);
51     timer.stop();
52 
53     double myAverage = static_cast<double>(timer.nanoseconds()) / n / rounds;
54     log << "-- My function --\n";
55     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
56     log << "     Average runtime : " << myAverage << " ns/op \n";
57     log << "     Ops per second  : "
58         << static_cast<uint64_t>(1'000'000'000.0 / myAverage) << " op/s \n";
59 
60     timer.start();
61     runner(otherFunc);
62     timer.stop();
63 
64     double otherAverage = static_cast<double>(timer.nanoseconds()) / n / rounds;
65     log << "-- Other function --\n";
66     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
67     log << "     Average runtime : " << otherAverage << " ns/op \n";
68     log << "     Ops per second  : "
69         << static_cast<uint64_t>(1'000'000'000.0 / otherAverage) << " op/s \n";
70 
71     log << "-- Average runtime ratio --\n";
72     log << "     Mine / Other's  : " << myAverage / otherAverage << " \n";
73   }
74 
runPerf(Func myFunc,Func otherFunc,size_t rounds,const char * logFile)75   static void runPerf(Func myFunc, Func otherFunc, size_t rounds,
76                       const char *logFile) {
77     std::ofstream log(logFile);
78     log << " Performance tests with inputs in denormal range:\n";
79     runPerfInRange(myFunc, otherFunc, /* startingBit= */ StorageType(0),
80                    /* endingBit= */ FPBits::max_subnormal().uintval(), rounds,
81                    log);
82     log << "\n Performance tests with inputs in normal range:\n";
83     runPerfInRange(myFunc, otherFunc,
84                    /* startingBit= */ FPBits::min_normal().uintval(),
85                    /* endingBit= */ FPBits::max_normal().uintval(), rounds,
86                    log);
87   }
88 };
89 
90 } // namespace testing
91 } // namespace LIBC_NAMESPACE_DECL
92 
93 #define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename)        \
94   int main() {                                                                 \
95     LIBC_NAMESPACE::testing::SingleInputSingleOutputPerf<T>::runPerf(          \
96         &myFunc, &otherFunc, 1, filename);                                     \
97     return 0;                                                                  \
98   }
99 
100 #define SINGLE_INPUT_SINGLE_OUTPUT_PERF_EX(T, myFunc, otherFunc, rounds,       \
101                                            filename)                           \
102   {                                                                            \
103     LIBC_NAMESPACE::testing::SingleInputSingleOutputPerf<T>::runPerf(          \
104         &myFunc, &otherFunc, rounds, filename);                                \
105   }
106