1 //===-- Common utility class for differential analysis --------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "src/__support/FPUtil/FPBits.h"
10 #include "test/src/math/differential_testing/Timer.h"
14 namespace LIBC_NAMESPACE
{
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
;
26 static uint64_t run_diff_in_range(Func myFunc
, Func otherFunc
,
27 UIntType startingBit
, UIntType endingBit
,
28 UIntType N
, std::ofstream
&log
) {
30 if (endingBit
< startingBit
) {
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()) {
43 log
<< " Input: " << bitsX
<< ", " << bitsY
<< " (" << x
<< ", "
45 << " My result: " << myBits
.uintval() << " (" << myBits
.get_val()
47 << "Other result: " << otherBits
.uintval() << " ("
48 << otherBits
.get_val() << ")\n"
52 if (endingBit
- bitsX
< step
) {
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
) {
64 if (endingBit
< startingBit
) {
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
));
74 if (endingBit
- bitsX
< step
) {
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";
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";
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) \
148 LIBC_NAMESPACE::testing::BinaryOpSingleOutputDiff<T>::run_diff( \
149 &myFunc, &otherFunc, filename); \
153 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \
155 LIBC_NAMESPACE::testing::BinaryOpSingleOutputDiff<T>::run_perf( \
156 &myFunc, &otherFunc, filename); \