xref: /llvm-project/libc/test/src/math/performance_testing/BinaryOpSingleOutputPerf.h (revision 1ace91f925ad87c3e5eb836ad58fdffe60c4aea6)
1d99bb014Slntue //===-- Common utility class for differential analysis --------------------===//
2d99bb014Slntue //
3d99bb014Slntue // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d99bb014Slntue // See https://llvm.org/LICENSE.txt for license information.
5d99bb014Slntue // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d99bb014Slntue //
7d99bb014Slntue //===----------------------------------------------------------------------===//
8d99bb014Slntue 
970843bf6SOverMighty #include "src/__support/CPP/algorithm.h"
10d99bb014Slntue #include "src/__support/FPUtil/FPBits.h"
115ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
12d99bb014Slntue #include "test/src/math/performance_testing/Timer.h"
13d99bb014Slntue 
140cdb0b74SOverMighty #include <cstddef>
15d99bb014Slntue #include <fstream>
16d99bb014Slntue 
175ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
18d99bb014Slntue namespace testing {
19*1ace91f9SJob Henandez Lara template <typename OutputType, typename InputType>
20*1ace91f9SJob Henandez Lara class BinaryOpSingleOutputPerf {
21*1ace91f9SJob Henandez Lara   using FPBits = fputil::FPBits<OutputType>;
22d99bb014Slntue   using StorageType = typename FPBits::StorageType;
23d99bb014Slntue   static constexpr StorageType UIntMax =
24d99bb014Slntue       cpp::numeric_limits<StorageType>::max();
25d99bb014Slntue 
26d99bb014Slntue public:
27*1ace91f9SJob Henandez Lara   typedef OutputType Func(InputType, InputType);
28d99bb014Slntue 
29d99bb014Slntue   static void run_perf_in_range(Func myFunc, Func otherFunc,
30d99bb014Slntue                                 StorageType startingBit, StorageType endingBit,
310cdb0b74SOverMighty                                 size_t N, size_t rounds, std::ofstream &log) {
3270843bf6SOverMighty     if (sizeof(StorageType) <= sizeof(size_t))
3370843bf6SOverMighty       N = cpp::min(N, static_cast<size_t>(endingBit - startingBit));
340cdb0b74SOverMighty 
35d99bb014Slntue     auto runner = [=](Func func) {
36*1ace91f9SJob Henandez Lara       [[maybe_unused]] volatile OutputType result;
37d99bb014Slntue       if (endingBit < startingBit) {
38d99bb014Slntue         return;
39d99bb014Slntue       }
40d99bb014Slntue 
41d99bb014Slntue       StorageType step = (endingBit - startingBit) / N;
420cdb0b74SOverMighty       for (size_t i = 0; i < rounds; i++) {
43d99bb014Slntue         for (StorageType bitsX = startingBit, bitsY = endingBit;;
44d99bb014Slntue              bitsX += step, bitsY -= step) {
45*1ace91f9SJob Henandez Lara           InputType x = FPBits(bitsX).get_val();
46*1ace91f9SJob Henandez Lara           InputType y = FPBits(bitsY).get_val();
47d99bb014Slntue           result = func(x, y);
48d99bb014Slntue           if (endingBit - bitsX < step) {
49d99bb014Slntue             break;
50d99bb014Slntue           }
51d99bb014Slntue         }
520cdb0b74SOverMighty       }
53d99bb014Slntue     };
54d99bb014Slntue 
55d99bb014Slntue     Timer timer;
56d99bb014Slntue     timer.start();
57d99bb014Slntue     runner(myFunc);
58d99bb014Slntue     timer.stop();
59d99bb014Slntue 
600cdb0b74SOverMighty     double my_average = static_cast<double>(timer.nanoseconds()) / N / rounds;
61d99bb014Slntue     log << "-- My function --\n";
62d99bb014Slntue     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
63d99bb014Slntue     log << "     Average runtime : " << my_average << " ns/op \n";
64d99bb014Slntue     log << "     Ops per second  : "
65d99bb014Slntue         << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n";
66d99bb014Slntue 
67d99bb014Slntue     timer.start();
68d99bb014Slntue     runner(otherFunc);
69d99bb014Slntue     timer.stop();
70d99bb014Slntue 
710cdb0b74SOverMighty     double other_average =
720cdb0b74SOverMighty         static_cast<double>(timer.nanoseconds()) / N / rounds;
73d99bb014Slntue     log << "-- Other function --\n";
74d99bb014Slntue     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
75d99bb014Slntue     log << "     Average runtime : " << other_average << " ns/op \n";
76d99bb014Slntue     log << "     Ops per second  : "
77d99bb014Slntue         << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n";
78d99bb014Slntue 
79d99bb014Slntue     log << "-- Average runtime ratio --\n";
80d99bb014Slntue     log << "     Mine / Other's  : " << my_average / other_average << " \n";
81d99bb014Slntue   }
82d99bb014Slntue 
830cdb0b74SOverMighty   static void run_perf(Func myFunc, Func otherFunc, int rounds,
840cdb0b74SOverMighty                        const char *logFile) {
85d99bb014Slntue     std::ofstream log(logFile);
86d99bb014Slntue     log << " Performance tests with inputs in denormal range:\n";
87d99bb014Slntue     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ StorageType(0),
88d99bb014Slntue                       /* endingBit= */ FPBits::max_subnormal().uintval(),
890cdb0b74SOverMighty                       1'000'001, rounds, log);
90d99bb014Slntue     log << "\n Performance tests with inputs in normal range:\n";
91d99bb014Slntue     run_perf_in_range(myFunc, otherFunc,
92d99bb014Slntue                       /* startingBit= */ FPBits::min_normal().uintval(),
93d99bb014Slntue                       /* endingBit= */ FPBits::max_normal().uintval(),
940cdb0b74SOverMighty                       1'000'001, rounds, log);
95d99bb014Slntue     log << "\n Performance tests with inputs in normal range with exponents "
96d99bb014Slntue            "close to each other:\n";
97*1ace91f9SJob Henandez Lara     run_perf_in_range(
98*1ace91f9SJob Henandez Lara         myFunc, otherFunc,
99*1ace91f9SJob Henandez Lara         /* startingBit= */ FPBits(OutputType(0x1.0p-10)).uintval(),
100*1ace91f9SJob Henandez Lara         /* endingBit= */ FPBits(OutputType(0x1.0p+10)).uintval(), 1'000'001,
101*1ace91f9SJob Henandez Lara         rounds, log);
102d99bb014Slntue   }
103d99bb014Slntue 
104d99bb014Slntue   static void run_diff(Func myFunc, Func otherFunc, const char *logFile) {
105d99bb014Slntue     uint64_t diffCount = 0;
106d99bb014Slntue     std::ofstream log(logFile);
107d99bb014Slntue     log << " Diff tests with inputs in denormal range:\n";
108d99bb014Slntue     diffCount += run_diff_in_range(
109d99bb014Slntue         myFunc, otherFunc, /* startingBit= */ StorageType(0),
110d99bb014Slntue         /* endingBit= */ FPBits::max_subnormal().uintval(), 1'000'001, log);
111d99bb014Slntue     log << "\n Diff tests with inputs in normal range:\n";
112d99bb014Slntue     diffCount += run_diff_in_range(
113d99bb014Slntue         myFunc, otherFunc,
114d99bb014Slntue         /* startingBit= */ FPBits::min_normal().uintval(),
115d99bb014Slntue         /* endingBit= */ FPBits::max_normal().uintval(), 100'000'001, log);
116d99bb014Slntue     log << "\n Diff tests with inputs in normal range with exponents "
117d99bb014Slntue            "close to each other:\n";
118d99bb014Slntue     diffCount += run_diff_in_range(
119*1ace91f9SJob Henandez Lara         myFunc, otherFunc,
120*1ace91f9SJob Henandez Lara         /* startingBit= */ FPBits(OutputType(0x1.0p-10)).uintval(),
121*1ace91f9SJob Henandez Lara         /* endingBit= */ FPBits(OutputType(0x1.0p+10)).uintval(), 10'000'001,
122*1ace91f9SJob Henandez Lara         log);
123d99bb014Slntue 
124d99bb014Slntue     log << "Total number of differing results: " << diffCount << '\n';
125d99bb014Slntue   }
126d99bb014Slntue };
127d99bb014Slntue 
128d99bb014Slntue } // namespace testing
1295ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
130d99bb014Slntue 
131*1ace91f9SJob Henandez Lara #define BINARY_OP_SINGLE_OUTPUT_PERF(OutputType, InputType, myFunc, otherFunc, \
132*1ace91f9SJob Henandez Lara                                      filename)                                 \
133d99bb014Slntue   int main() {                                                                 \
134*1ace91f9SJob Henandez Lara     LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<                         \
135*1ace91f9SJob Henandez Lara         OutputType, InputType>::run_perf(&myFunc, &otherFunc, 1, filename);    \
136d99bb014Slntue     return 0;                                                                  \
137d99bb014Slntue   }
1380cdb0b74SOverMighty 
139*1ace91f9SJob Henandez Lara #define BINARY_OP_SINGLE_OUTPUT_PERF_EX(OutputType, InputType, myFunc,         \
140*1ace91f9SJob Henandez Lara                                         otherFunc, rounds, filename)           \
1410cdb0b74SOverMighty   {                                                                            \
142*1ace91f9SJob Henandez Lara     LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<                         \
143*1ace91f9SJob Henandez Lara         OutputType, InputType>::run_perf(&myFunc, &otherFunc, rounds,          \
144*1ace91f9SJob Henandez Lara                                          filename);                            \
145*1ace91f9SJob Henandez Lara     LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<                         \
146*1ace91f9SJob Henandez Lara         OutputType, InputType>::run_perf(&myFunc, &otherFunc, rounds,          \
147*1ace91f9SJob Henandez Lara                                          filename);                            \
1480cdb0b74SOverMighty   }
149