1c6aa206bSSiva Chandra Reddy //===-- Common utility class for differential analysis --------------------===//
2c6aa206bSSiva Chandra Reddy //
3c6aa206bSSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c6aa206bSSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
5c6aa206bSSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c6aa206bSSiva Chandra Reddy //
7c6aa206bSSiva Chandra Reddy //===----------------------------------------------------------------------===//
8c6aa206bSSiva Chandra Reddy 
9c120edc7SMichael Jones #include "src/__support/FPUtil/FPBits.h"
10c6aa206bSSiva Chandra Reddy #include "utils/testutils/StreamWrapper.h"
11c6aa206bSSiva Chandra Reddy #include "utils/testutils/Timer.h"
12c6aa206bSSiva Chandra Reddy 
13c6aa206bSSiva Chandra Reddy namespace __llvm_libc {
14c6aa206bSSiva Chandra Reddy namespace testing {
15c6aa206bSSiva Chandra Reddy 
16c6aa206bSSiva Chandra Reddy template <typename T> class SingleInputSingleOutputDiff {
17c6aa206bSSiva Chandra Reddy   using FPBits = fputil::FPBits<T>;
18c6aa206bSSiva Chandra Reddy   using UIntType = typename FPBits::UIntType;
19c6aa206bSSiva Chandra Reddy   static constexpr UIntType MSBit = UIntType(1) << (8 * sizeof(UIntType) - 1);
20c6aa206bSSiva Chandra Reddy   static constexpr UIntType UIntMax = (MSBit - 1) + MSBit;
21c6aa206bSSiva Chandra Reddy 
22c6aa206bSSiva Chandra Reddy public:
23c6aa206bSSiva Chandra Reddy   typedef T Func(T);
24c6aa206bSSiva Chandra Reddy 
runDiff(Func myFunc,Func otherFunc,const char * logFile)25c6aa206bSSiva Chandra Reddy   static void runDiff(Func myFunc, Func otherFunc, const char *logFile) {
26c6aa206bSSiva Chandra Reddy     UIntType diffCount = 0;
27c6aa206bSSiva Chandra Reddy     testutils::OutputFileStream log(logFile);
28c6aa206bSSiva Chandra Reddy     log << "Starting diff for values from 0 to " << UIntMax << '\n'
29c6aa206bSSiva Chandra Reddy         << "Only differing results will be logged.\n\n";
30c6aa206bSSiva Chandra Reddy     for (UIntType bits = 0;; ++bits) {
31c6aa206bSSiva Chandra Reddy       T x = T(FPBits(bits));
32c6aa206bSSiva Chandra Reddy       T myResult = myFunc(x);
33c6aa206bSSiva Chandra Reddy       T otherResult = otherFunc(x);
34c6aa206bSSiva Chandra Reddy       UIntType myBits = FPBits(myResult).uintval();
35c6aa206bSSiva Chandra Reddy       UIntType otherBits = FPBits(otherResult).uintval();
36c6aa206bSSiva Chandra Reddy       if (myBits != otherBits) {
37c6aa206bSSiva Chandra Reddy         ++diffCount;
38c6aa206bSSiva Chandra Reddy         log << "       Input: " << bits << " (" << x << ")\n"
39c6aa206bSSiva Chandra Reddy             << "   My result: " << myBits << " (" << myResult << ")\n"
40c6aa206bSSiva Chandra Reddy             << "Other result: " << otherBits << " (" << otherResult << ")\n"
41c6aa206bSSiva Chandra Reddy             << '\n';
42c6aa206bSSiva Chandra Reddy       }
43c6aa206bSSiva Chandra Reddy       if (bits == UIntMax)
44c6aa206bSSiva Chandra Reddy         break;
45c6aa206bSSiva Chandra Reddy     }
46c6aa206bSSiva Chandra Reddy     log << "Total number of differing results: " << diffCount << '\n';
47c6aa206bSSiva Chandra Reddy   }
48c6aa206bSSiva Chandra Reddy 
runPerfInRange(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,testutils::OutputFileStream & log)49*db8716a2STue Ly   static void runPerfInRange(Func myFunc, Func otherFunc, UIntType startingBit,
50*db8716a2STue Ly                              UIntType endingBit,
51*db8716a2STue Ly                              testutils::OutputFileStream &log) {
52*db8716a2STue Ly     auto runner = [=](Func func) {
53c6aa206bSSiva Chandra Reddy       volatile T result;
54*db8716a2STue Ly       for (UIntType bits = startingBit;; ++bits) {
55c6aa206bSSiva Chandra Reddy         T x = T(FPBits(bits));
56c6aa206bSSiva Chandra Reddy         result = func(x);
57*db8716a2STue Ly         if (bits == endingBit)
58c6aa206bSSiva Chandra Reddy           break;
59c6aa206bSSiva Chandra Reddy       }
60c6aa206bSSiva Chandra Reddy     };
61c6aa206bSSiva Chandra Reddy 
62c6aa206bSSiva Chandra Reddy     Timer timer;
63c6aa206bSSiva Chandra Reddy     timer.start();
64c6aa206bSSiva Chandra Reddy     runner(myFunc);
65c6aa206bSSiva Chandra Reddy     timer.stop();
66*db8716a2STue Ly 
67*db8716a2STue Ly     UIntType numberOfRuns = endingBit - startingBit + 1;
68*db8716a2STue Ly     double myAverage = static_cast<double>(timer.nanoseconds()) / numberOfRuns;
69*db8716a2STue Ly     log << "-- My function --\n";
70*db8716a2STue Ly     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
71*db8716a2STue Ly     log << "     Average runtime : " << myAverage << " ns/op \n";
72*db8716a2STue Ly     log << "     Ops per second  : "
73*db8716a2STue Ly         << static_cast<uint64_t>(1'000'000'000.0 / myAverage) << " op/s \n";
74c6aa206bSSiva Chandra Reddy 
75c6aa206bSSiva Chandra Reddy     timer.start();
76c6aa206bSSiva Chandra Reddy     runner(otherFunc);
77c6aa206bSSiva Chandra Reddy     timer.stop();
78*db8716a2STue Ly 
79*db8716a2STue Ly     double otherAverage =
80*db8716a2STue Ly         static_cast<double>(timer.nanoseconds()) / numberOfRuns;
81*db8716a2STue Ly     log << "-- Other function --\n";
82*db8716a2STue Ly     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
83*db8716a2STue Ly     log << "     Average runtime : " << otherAverage << " ns/op \n";
84*db8716a2STue Ly     log << "     Ops per second  : "
85*db8716a2STue Ly         << static_cast<uint64_t>(1'000'000'000.0 / otherAverage) << " op/s \n";
86*db8716a2STue Ly 
87*db8716a2STue Ly     log << "-- Average runtime ratio --\n";
88*db8716a2STue Ly     log << "     Mine / Other's  : " << myAverage / otherAverage << " \n";
89*db8716a2STue Ly   }
90*db8716a2STue Ly 
runPerf(Func myFunc,Func otherFunc,const char * logFile)91*db8716a2STue Ly   static void runPerf(Func myFunc, Func otherFunc, const char *logFile) {
92*db8716a2STue Ly     testutils::OutputFileStream log(logFile);
93*db8716a2STue Ly     log << " Performance tests with inputs in denormal range:\n";
94*db8716a2STue Ly     runPerfInRange(myFunc, otherFunc, /* startingBit= */ UIntType(0),
95*db8716a2STue Ly                    /* endingBit= */ FPBits::MAX_SUBNORMAL, log);
96*db8716a2STue Ly     log << "\n Performance tests with inputs in normal range:\n";
97*db8716a2STue Ly     runPerfInRange(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
98*db8716a2STue Ly                    /* endingBit= */ FPBits::MAX_NORMAL, log);
99c6aa206bSSiva Chandra Reddy   }
100c6aa206bSSiva Chandra Reddy };
101c6aa206bSSiva Chandra Reddy 
102c6aa206bSSiva Chandra Reddy } // namespace testing
103c6aa206bSSiva Chandra Reddy } // namespace __llvm_libc
104c6aa206bSSiva Chandra Reddy 
105c6aa206bSSiva Chandra Reddy #define SINGLE_INPUT_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename)        \
106c6aa206bSSiva Chandra Reddy   int main() {                                                                 \
107c6aa206bSSiva Chandra Reddy     __llvm_libc::testing::SingleInputSingleOutputDiff<T>::runDiff(             \
108c6aa206bSSiva Chandra Reddy         &myFunc, &otherFunc, filename);                                        \
109c6aa206bSSiva Chandra Reddy     return 0;                                                                  \
110c6aa206bSSiva Chandra Reddy   }
111c6aa206bSSiva Chandra Reddy 
112c6aa206bSSiva Chandra Reddy #define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename)        \
113c6aa206bSSiva Chandra Reddy   int main() {                                                                 \
114c6aa206bSSiva Chandra Reddy     __llvm_libc::testing::SingleInputSingleOutputDiff<T>::runPerf(             \
115c6aa206bSSiva Chandra Reddy         &myFunc, &otherFunc, filename);                                        \
116c6aa206bSSiva Chandra Reddy     return 0;                                                                  \
117c6aa206bSSiva Chandra Reddy   }
118