Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libc / test / src / math / differential_testing / BinaryOpSingleOutputDiff.h
blob1ea50deccb7eaf840c91af981a2df03881126a14
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 //===----------------------------------------------------------------------===//
9 #include "src/__support/FPUtil/FPBits.h"
10 #include "test/src/math/differential_testing/Timer.h"
12 #include <fstream>
14 namespace LIBC_NAMESPACE {
15 namespace testing {
17 template <typename T> class BinaryOpSingleOutputDiff {
18 using FPBits = fputil::FPBits<T>;
19 using UIntType = typename FPBits::UIntType;
20 static constexpr UIntType MSBIT = UIntType(1) << (8 * sizeof(UIntType) - 1);
21 static constexpr UIntType UINTMAX = (MSBIT - 1) + MSBIT;
23 public:
24 typedef T Func(T, T);
26 static uint64_t run_diff_in_range(Func myFunc, Func otherFunc,
27 UIntType startingBit, UIntType endingBit,
28 UIntType N, std::ofstream &log) {
29 uint64_t result = 0;
30 if (endingBit < startingBit) {
31 return result;
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';
52 if (endingBit - bitsX < step) {
53 break;
56 return result;
59 static void run_perf_in_range(Func myFunc, Func otherFunc,
60 UIntType startingBit, UIntType endingBit,
61 UIntType N, std::ofstream &log) {
62 auto runner = [=](Func func) {
63 volatile T result;
64 if (endingBit < startingBit) {
65 return;
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;
80 Timer timer;
81 timer.start();
82 runner(myFunc);
83 timer.stop();
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";
92 timer.start();
93 runner(otherFunc);
94 timer.stop();
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";
103 log << "-- Average runtime ratio --\n";
104 log << " Mine / Other's : " << my_average / other_average << " \n";
107 static void run_perf(Func myFunc, Func otherFunc, const char *logFile) {
108 std::ofstream 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);
122 static void run_diff(Func myFunc, Func otherFunc, const char *logFile) {
123 uint64_t diffCount = 0;
124 std::ofstream 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);
139 log << "Total number of differing results: " << diffCount << '\n';
143 } // namespace testing
144 } // namespace LIBC_NAMESPACE
146 #define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename) \
147 int main() { \
148 LIBC_NAMESPACE::testing::BinaryOpSingleOutputDiff<T>::run_diff( \
149 &myFunc, &otherFunc, filename); \
150 return 0; \
153 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \
154 int main() { \
155 LIBC_NAMESPACE::testing::BinaryOpSingleOutputDiff<T>::run_perf( \
156 &myFunc, &otherFunc, filename); \
157 return 0; \