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 | |
14 | namespace LIBC_NAMESPACE { |
15 | namespace testing { |
16 | |
17 | template <typename T> class SingleInputSingleOutputPerf { |
18 | using FPBits = fputil::FPBits<T>; |
19 | using StorageType = typename FPBits::StorageType; |
20 | static constexpr StorageType UIntMax = |
21 | cpp::numeric_limits<StorageType>::max(); |
22 | |
23 | public: |
24 | typedef T Func(T); |
25 | |
26 | static void runPerfInRange(Func myFunc, Func otherFunc, |
27 | StorageType startingBit, StorageType endingBit, |
28 | std::ofstream &log) { |
29 | auto runner = [=](Func func) { |
30 | constexpr StorageType N = 10'010'001; |
31 | StorageType step = (endingBit - startingBit) / N; |
32 | if (step == 0) |
33 | step = 1; |
34 | volatile T result; |
35 | for (StorageType bits = startingBit; bits < endingBit; bits += step) { |
36 | T x = FPBits(bits).get_val(); |
37 | result = func(x); |
38 | } |
39 | }; |
40 | |
41 | Timer timer; |
42 | timer.start(); |
43 | runner(myFunc); |
44 | timer.stop(); |
45 | |
46 | StorageType numberOfRuns = endingBit - startingBit + 1; |
47 | double myAverage = static_cast<double>(timer.nanoseconds()) / numberOfRuns; |
48 | log << "-- My function --\n" ; |
49 | log << " Total time : " << timer.nanoseconds() << " ns \n" ; |
50 | log << " Average runtime : " << myAverage << " ns/op \n" ; |
51 | log << " Ops per second : " |
52 | << static_cast<uint64_t>(1'000'000'000.0 / myAverage) << " op/s \n" ; |
53 | |
54 | timer.start(); |
55 | runner(otherFunc); |
56 | timer.stop(); |
57 | |
58 | double otherAverage = |
59 | static_cast<double>(timer.nanoseconds()) / numberOfRuns; |
60 | log << "-- Other function --\n" ; |
61 | log << " Total time : " << timer.nanoseconds() << " ns \n" ; |
62 | log << " Average runtime : " << otherAverage << " ns/op \n" ; |
63 | log << " Ops per second : " |
64 | << static_cast<uint64_t>(1'000'000'000.0 / otherAverage) << " op/s \n" ; |
65 | |
66 | log << "-- Average runtime ratio --\n" ; |
67 | log << " Mine / Other's : " << myAverage / otherAverage << " \n" ; |
68 | } |
69 | |
70 | static void runPerf(Func myFunc, Func otherFunc, const char *logFile) { |
71 | std::ofstream log(logFile); |
72 | log << " Performance tests with inputs in denormal range:\n" ; |
73 | runPerfInRange(myFunc, otherFunc, /* startingBit= */ startingBit: StorageType(0), |
74 | /* endingBit= */ endingBit: FPBits::max_subnormal().uintval(), log); |
75 | log << "\n Performance tests with inputs in normal range:\n" ; |
76 | runPerfInRange(myFunc, otherFunc, |
77 | /* startingBit= */ startingBit: FPBits::min_normal().uintval(), |
78 | /* endingBit= */ endingBit: FPBits::max_normal().uintval(), log); |
79 | } |
80 | }; |
81 | |
82 | } // namespace testing |
83 | } // namespace LIBC_NAMESPACE |
84 | |
85 | #define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \ |
86 | int main() { \ |
87 | LIBC_NAMESPACE::testing::SingleInputSingleOutputPerf<T>::runPerf( \ |
88 | &myFunc, &otherFunc, filename); \ |
89 | return 0; \ |
90 | } |
91 | |