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

source code of libc/test/src/math/performance_testing/BinaryOpSingleOutputPerf.h