• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "test/src/math/performance_testing/Timer.h"
11 
12 #include <cstddef>
13 #include <fstream>
14 
15 namespace LIBC_NAMESPACE {
16 namespace testing {
17 
18 template <typename T> class BinaryOpSingleOutputPerf {
19   using FPBits = fputil::FPBits<T>;
20   using StorageType = typename FPBits::StorageType;
21   static constexpr StorageType UIntMax =
22       cpp::numeric_limits<StorageType>::max();
23 
24 public:
25   typedef T Func(T, T);
26 
run_perf_in_range(Func myFunc,Func otherFunc,StorageType startingBit,StorageType endingBit,size_t N,size_t rounds,std::ofstream & log)27   static void run_perf_in_range(Func myFunc, Func otherFunc,
28                                 StorageType startingBit, StorageType endingBit,
29                                 size_t N, size_t rounds, std::ofstream &log) {
30     if (endingBit - startingBit < N)
31       N = endingBit - startingBit;
32 
33     auto runner = [=](Func func) {
34       volatile T result;
35       if (endingBit < startingBit) {
36         return;
37       }
38 
39       StorageType step = (endingBit - startingBit) / N;
40       for (size_t i = 0; i < rounds; i++) {
41         for (StorageType bitsX = startingBit, bitsY = endingBit;;
42              bitsX += step, bitsY -= step) {
43           T x = FPBits(bitsX).get_val();
44           T y = FPBits(bitsY).get_val();
45           result = func(x, y);
46           if (endingBit - bitsX < step) {
47             break;
48           }
49         }
50       }
51     };
52 
53     Timer timer;
54     timer.start();
55     runner(myFunc);
56     timer.stop();
57 
58     double my_average = static_cast<double>(timer.nanoseconds()) / N / rounds;
59     log << "-- My function --\n";
60     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
61     log << "     Average runtime : " << my_average << " ns/op \n";
62     log << "     Ops per second  : "
63         << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n";
64 
65     timer.start();
66     runner(otherFunc);
67     timer.stop();
68 
69     double other_average =
70         static_cast<double>(timer.nanoseconds()) / N / rounds;
71     log << "-- Other function --\n";
72     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
73     log << "     Average runtime : " << other_average << " ns/op \n";
74     log << "     Ops per second  : "
75         << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n";
76 
77     log << "-- Average runtime ratio --\n";
78     log << "     Mine / Other's  : " << my_average / other_average << " \n";
79   }
80 
run_perf(Func myFunc,Func otherFunc,int rounds,const char * logFile)81   static void run_perf(Func myFunc, Func otherFunc, int rounds,
82                        const char *logFile) {
83     std::ofstream log(logFile);
84     log << " Performance tests with inputs in denormal range:\n";
85     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ StorageType(0),
86                       /* endingBit= */ FPBits::max_subnormal().uintval(),
87                       1'000'001, rounds, log);
88     log << "\n Performance tests with inputs in normal range:\n";
89     run_perf_in_range(myFunc, otherFunc,
90                       /* startingBit= */ FPBits::min_normal().uintval(),
91                       /* endingBit= */ FPBits::max_normal().uintval(),
92                       1'000'001, rounds, log);
93     log << "\n Performance tests with inputs in normal range with exponents "
94            "close to each other:\n";
95     run_perf_in_range(myFunc, otherFunc,
96                       /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
97                       /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(),
98                       1'000'001, rounds, log);
99   }
100 
run_diff(Func myFunc,Func otherFunc,const char * logFile)101   static void run_diff(Func myFunc, Func otherFunc, const char *logFile) {
102     uint64_t diffCount = 0;
103     std::ofstream log(logFile);
104     log << " Diff tests with inputs in denormal range:\n";
105     diffCount += run_diff_in_range(
106         myFunc, otherFunc, /* startingBit= */ StorageType(0),
107         /* endingBit= */ FPBits::max_subnormal().uintval(), 1'000'001, log);
108     log << "\n Diff tests with inputs in normal range:\n";
109     diffCount += run_diff_in_range(
110         myFunc, otherFunc,
111         /* startingBit= */ FPBits::min_normal().uintval(),
112         /* endingBit= */ FPBits::max_normal().uintval(), 100'000'001, log);
113     log << "\n Diff tests with inputs in normal range with exponents "
114            "close to each other:\n";
115     diffCount += run_diff_in_range(
116         myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
117         /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
118 
119     log << "Total number of differing results: " << diffCount << '\n';
120   }
121 };
122 
123 } // namespace testing
124 } // namespace LIBC_NAMESPACE
125 
126 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename)           \
127   int main() {                                                                 \
128     LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<T>::run_perf(            \
129         &myFunc, &otherFunc, 1, filename);                                     \
130     return 0;                                                                  \
131   }
132 
133 #define BINARY_OP_SINGLE_OUTPUT_PERF_EX(T, myFunc, otherFunc, rounds,          \
134                                         filename)                              \
135   {                                                                            \
136     LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<T>::run_perf(            \
137         &myFunc, &otherFunc, rounds, filename);                                \
138     LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<T>::run_perf(            \
139         &myFunc, &otherFunc, rounds, filename);                                \
140   }
141