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 
run_diff_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)25   static uint64_t run_diff_in_range(Func myFunc, Func otherFunc,
26                                     UIntType startingBit, UIntType endingBit,
27                                     UIntType N,
28                                     testutils::OutputFileStream &log) {
29     uint64_t result = 0;
30     if (endingBit < startingBit) {
31       return result;
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       FPBits myBits = FPBits(myFunc(x, y));
40       FPBits otherBits = FPBits(otherFunc(x, y));
41       if (myBits.uintval() != otherBits.uintval()) {
42         result++;
43         log << "       Input: " << bitsX << ", " << bitsY << " (" << x << ", "
44             << y << ")\n"
45             << "   My result: " << myBits.uintval() << " (" << myBits.get_val()
46             << ")\n"
47             << "Other result: " << otherBits.uintval() << " ("
48             << otherBits.get_val() << ")\n"
49             << '\n';
50       }
51 
52       if (endingBit - bitsX < step) {
53         break;
54       }
55     }
56     return result;
57   }
58 
run_perf_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)59   static void run_perf_in_range(Func myFunc, Func otherFunc,
60                                 UIntType startingBit, UIntType endingBit,
61                                 UIntType N, testutils::OutputFileStream &log) {
62     auto runner = [=](Func func) {
63       volatile T result;
64       if (endingBit < startingBit) {
65         return;
66       }
67 
68       UIntType step = (endingBit - startingBit) / N;
69       for (UIntType bitsX = startingBit, bitsY = endingBit;;
70            bitsX += step, bitsY -= step) {
71         T x = T(FPBits(bitsX));
72         T y = T(FPBits(bitsY));
73         result = func(x, y);
74         if (endingBit - bitsX < step) {
75           break;
76         }
77       }
78     };
79 
80     Timer timer;
81     timer.start();
82     runner(myFunc);
83     timer.stop();
84 
85     double my_average = static_cast<double>(timer.nanoseconds()) / N;
86     log << "-- My function --\n";
87     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
88     log << "     Average runtime : " << my_average << " ns/op \n";
89     log << "     Ops per second  : "
90         << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n";
91 
92     timer.start();
93     runner(otherFunc);
94     timer.stop();
95 
96     double other_average = static_cast<double>(timer.nanoseconds()) / N;
97     log << "-- Other function --\n";
98     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
99     log << "     Average runtime : " << other_average << " ns/op \n";
100     log << "     Ops per second  : "
101         << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n";
102 
103     log << "-- Average runtime ratio --\n";
104     log << "     Mine / Other's  : " << my_average / other_average << " \n";
105   }
106 
run_perf(Func myFunc,Func otherFunc,const char * logFile)107   static void run_perf(Func myFunc, Func otherFunc, const char *logFile) {
108     testutils::OutputFileStream log(logFile);
109     log << " Performance tests with inputs in denormal range:\n";
110     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ UIntType(0),
111                       /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log);
112     log << "\n Performance tests with inputs in normal range:\n";
113     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
114                       /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log);
115     log << "\n Performance tests with inputs in normal range with exponents "
116            "close to each other:\n";
117     run_perf_in_range(
118         myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
119         /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
120   }
121 
run_diff(Func myFunc,Func otherFunc,const char * logFile)122   static void run_diff(Func myFunc, Func otherFunc, const char *logFile) {
123     uint64_t diffCount = 0;
124     testutils::OutputFileStream log(logFile);
125     log << " Diff tests with inputs in denormal range:\n";
126     diffCount += run_diff_in_range(
127         myFunc, otherFunc, /* startingBit= */ UIntType(0),
128         /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log);
129     log << "\n Diff tests with inputs in normal range:\n";
130     diffCount += run_diff_in_range(
131         myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
132         /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log);
133     log << "\n Diff tests with inputs in normal range with exponents "
134            "close to each other:\n";
135     diffCount += run_diff_in_range(
136         myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
137         /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
138 
139     log << "Total number of differing results: " << diffCount << '\n';
140   }
141 };
142 
143 } // namespace testing
144 } // namespace __llvm_libc
145 
146 #define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename)           \
147   int main() {                                                                 \
148     __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_diff(               \
149         &myFunc, &otherFunc, filename);                                        \
150     return 0;                                                                  \
151   }
152 
153 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename)           \
154   int main() {                                                                 \
155     __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_perf(               \
156         &myFunc, &otherFunc, filename);                                        \
157     return 0;                                                                  \
158   }
159