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 SingleInputSingleOutputDiff {
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);
24 
25   static void runDiff(Func myFunc, Func otherFunc, const char *logFile) {
26     UIntType diffCount = 0;
27     testutils::OutputFileStream log(logFile);
28     log << "Starting diff for values from 0 to " << UIntMax << '\n'
29         << "Only differing results will be logged.\n\n";
30     for (UIntType bits = 0;; ++bits) {
31       T x = T(FPBits(bits));
32       T myResult = myFunc(x);
33       T otherResult = otherFunc(x);
34       UIntType myBits = FPBits(myResult).uintval();
35       UIntType otherBits = FPBits(otherResult).uintval();
36       if (myBits != otherBits) {
37         ++diffCount;
38         log << "       Input: " << bits << " (" << x << ")\n"
39             << "   My result: " << myBits << " (" << myResult << ")\n"
40             << "Other result: " << otherBits << " (" << otherResult << ")\n"
41             << '\n';
42       }
43       if (bits == UIntMax)
44         break;
45     }
46     log << "Total number of differing results: " << diffCount << '\n';
47   }
48 
49   static void runPerfInRange(Func myFunc, Func otherFunc, UIntType startingBit,
50                              UIntType endingBit,
51                              testutils::OutputFileStream &log) {
52     auto runner = [=](Func func) {
53       volatile T result;
54       for (UIntType bits = startingBit;; ++bits) {
55         T x = T(FPBits(bits));
56         result = func(x);
57         if (bits == endingBit)
58           break;
59       }
60     };
61 
62     Timer timer;
63     timer.start();
64     runner(myFunc);
65     timer.stop();
66 
67     UIntType numberOfRuns = endingBit - startingBit + 1;
68     double myAverage = static_cast<double>(timer.nanoseconds()) / numberOfRuns;
69     log << "-- My function --\n";
70     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
71     log << "     Average runtime : " << myAverage << " ns/op \n";
72     log << "     Ops per second  : "
73         << static_cast<uint64_t>(1'000'000'000.0 / myAverage) << " op/s \n";
74 
75     timer.start();
76     runner(otherFunc);
77     timer.stop();
78 
79     double otherAverage =
80         static_cast<double>(timer.nanoseconds()) / numberOfRuns;
81     log << "-- Other function --\n";
82     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
83     log << "     Average runtime : " << otherAverage << " ns/op \n";
84     log << "     Ops per second  : "
85         << static_cast<uint64_t>(1'000'000'000.0 / otherAverage) << " op/s \n";
86 
87     log << "-- Average runtime ratio --\n";
88     log << "     Mine / Other's  : " << myAverage / otherAverage << " \n";
89   }
90 
91   static void runPerf(Func myFunc, Func otherFunc, const char *logFile) {
92     testutils::OutputFileStream log(logFile);
93     log << " Performance tests with inputs in denormal range:\n";
94     runPerfInRange(myFunc, otherFunc, /* startingBit= */ UIntType(0),
95                    /* endingBit= */ FPBits::MAX_SUBNORMAL, log);
96     log << "\n Performance tests with inputs in normal range:\n";
97     runPerfInRange(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
98                    /* endingBit= */ FPBits::MAX_NORMAL, log);
99   }
100 };
101 
102 } // namespace testing
103 } // namespace __llvm_libc
104 
105 #define SINGLE_INPUT_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename)        \
106   int main() {                                                                 \
107     __llvm_libc::testing::SingleInputSingleOutputDiff<T>::runDiff(             \
108         &myFunc, &otherFunc, filename);                                        \
109     return 0;                                                                  \
110   }
111 
112 #define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename)        \
113   int main() {                                                                 \
114     __llvm_libc::testing::SingleInputSingleOutputDiff<T>::runPerf(             \
115         &myFunc, &otherFunc, filename);                                        \
116     return 0;                                                                  \
117   }
118