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/FPUtil/FPBits.h"
10 #include "utils/testutils/StreamWrapper.h"
11 #include "utils/testutils/Timer.h"
12 
13 namespace __llvm_libc {
14 namespace testing {
15 
16 template <typename T> class BinaryOpSingleOutputDiff {
17   using FPBits = fputil::FPBits<T>;
18   using UIntType = typename FPBits::UIntType;
19   static constexpr UIntType MSBIT = UIntType(1) << (8 * sizeof(UIntType) - 1);
20   static constexpr UIntType UINTMAX = (MSBIT - 1) + MSBIT;
21 
22 public:
23   typedef T Func(T, T);
24 
25   static void run_perf_in_range(Func myFunc, Func otherFunc,
26                                 UIntType startingBit, UIntType endingBit,
27                                 UIntType N, testutils::OutputFileStream &log) {
28     auto runner = [=](Func func) {
29       volatile T result;
30       if (endingBit < startingBit) {
31         return;
32       }
33 
34       UIntType step = (endingBit - startingBit) / N;
35       for (UIntType bitsX = startingBit, bitsY = endingBit;;
36            bitsX += step, bitsY -= step) {
37         T x = T(FPBits(bitsX));
38         T y = T(FPBits(bitsY));
39         result = func(x, y);
40         if (endingBit - bitsX < step) {
41           break;
42         }
43       }
44     };
45 
46     Timer timer;
47     timer.start();
48     runner(myFunc);
49     timer.stop();
50 
51     double my_average = static_cast<double>(timer.nanoseconds()) / N;
52     log << "-- My function --\n";
53     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
54     log << "     Average runtime : " << my_average << " ns/op \n";
55     log << "     Ops per second  : "
56         << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n";
57 
58     timer.start();
59     runner(otherFunc);
60     timer.stop();
61 
62     double other_average = static_cast<double>(timer.nanoseconds()) / N;
63     log << "-- Other function --\n";
64     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
65     log << "     Average runtime : " << other_average << " ns/op \n";
66     log << "     Ops per second  : "
67         << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n";
68 
69     log << "-- Average runtime ratio --\n";
70     log << "     Mine / Other's  : " << my_average / other_average << " \n";
71   }
72 
73   static void run_perf(Func myFunc, Func otherFunc, const char *logFile) {
74     testutils::OutputFileStream log(logFile);
75     log << " Performance tests with inputs in denormal range:\n";
76     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ UIntType(0),
77                       /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log);
78     log << "\n Performance tests with inputs in normal range:\n";
79     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
80                       /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log);
81     log << "\n Performance tests with inputs in normal range with exponents "
82            "close to each other:\n";
83     run_perf_in_range(
84         myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
85         /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
86   }
87 };
88 
89 } // namespace testing
90 } // namespace __llvm_libc
91 
92 #define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename)           \
93   int main() {                                                                 \
94     __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_diff(               \
95         &myFunc, &otherFunc, filename);                                        \
96     return 0;                                                                  \
97   }
98 
99 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename)           \
100   int main() {                                                                 \
101     __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_perf(               \
102         &myFunc, &otherFunc, filename);                                        \
103     return 0;                                                                  \
104   }
105