1d99bb014Slntue //===-- Common utility class for differential analysis --------------------===// 2d99bb014Slntue // 3d99bb014Slntue // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4d99bb014Slntue // See https://llvm.org/LICENSE.txt for license information. 5d99bb014Slntue // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6d99bb014Slntue // 7d99bb014Slntue //===----------------------------------------------------------------------===// 8d99bb014Slntue 970843bf6SOverMighty #include "src/__support/CPP/algorithm.h" 10d99bb014Slntue #include "src/__support/FPUtil/FPBits.h" 115ff3ff33SPetr Hosek #include "src/__support/macros/config.h" 12d99bb014Slntue #include "test/src/math/performance_testing/Timer.h" 13d99bb014Slntue 140cdb0b74SOverMighty #include <cstddef> 15d99bb014Slntue #include <fstream> 16d99bb014Slntue 175ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL { 18d99bb014Slntue namespace testing { 19*1ace91f9SJob Henandez Lara template <typename OutputType, typename InputType> 20*1ace91f9SJob Henandez Lara class BinaryOpSingleOutputPerf { 21*1ace91f9SJob Henandez Lara using FPBits = fputil::FPBits<OutputType>; 22d99bb014Slntue using StorageType = typename FPBits::StorageType; 23d99bb014Slntue static constexpr StorageType UIntMax = 24d99bb014Slntue cpp::numeric_limits<StorageType>::max(); 25d99bb014Slntue 26d99bb014Slntue public: 27*1ace91f9SJob Henandez Lara typedef OutputType Func(InputType, InputType); 28d99bb014Slntue 29d99bb014Slntue static void run_perf_in_range(Func myFunc, Func otherFunc, 30d99bb014Slntue StorageType startingBit, StorageType endingBit, 310cdb0b74SOverMighty size_t N, size_t rounds, std::ofstream &log) { 3270843bf6SOverMighty if (sizeof(StorageType) <= sizeof(size_t)) 3370843bf6SOverMighty N = cpp::min(N, static_cast<size_t>(endingBit - startingBit)); 340cdb0b74SOverMighty 35d99bb014Slntue auto runner = [=](Func func) { 36*1ace91f9SJob Henandez Lara [[maybe_unused]] volatile OutputType result; 37d99bb014Slntue if (endingBit < startingBit) { 38d99bb014Slntue return; 39d99bb014Slntue } 40d99bb014Slntue 41d99bb014Slntue StorageType step = (endingBit - startingBit) / N; 420cdb0b74SOverMighty for (size_t i = 0; i < rounds; i++) { 43d99bb014Slntue for (StorageType bitsX = startingBit, bitsY = endingBit;; 44d99bb014Slntue bitsX += step, bitsY -= step) { 45*1ace91f9SJob Henandez Lara InputType x = FPBits(bitsX).get_val(); 46*1ace91f9SJob Henandez Lara InputType y = FPBits(bitsY).get_val(); 47d99bb014Slntue result = func(x, y); 48d99bb014Slntue if (endingBit - bitsX < step) { 49d99bb014Slntue break; 50d99bb014Slntue } 51d99bb014Slntue } 520cdb0b74SOverMighty } 53d99bb014Slntue }; 54d99bb014Slntue 55d99bb014Slntue Timer timer; 56d99bb014Slntue timer.start(); 57d99bb014Slntue runner(myFunc); 58d99bb014Slntue timer.stop(); 59d99bb014Slntue 600cdb0b74SOverMighty double my_average = static_cast<double>(timer.nanoseconds()) / N / rounds; 61d99bb014Slntue log << "-- My function --\n"; 62d99bb014Slntue log << " Total time : " << timer.nanoseconds() << " ns \n"; 63d99bb014Slntue log << " Average runtime : " << my_average << " ns/op \n"; 64d99bb014Slntue log << " Ops per second : " 65d99bb014Slntue << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n"; 66d99bb014Slntue 67d99bb014Slntue timer.start(); 68d99bb014Slntue runner(otherFunc); 69d99bb014Slntue timer.stop(); 70d99bb014Slntue 710cdb0b74SOverMighty double other_average = 720cdb0b74SOverMighty static_cast<double>(timer.nanoseconds()) / N / rounds; 73d99bb014Slntue log << "-- Other function --\n"; 74d99bb014Slntue log << " Total time : " << timer.nanoseconds() << " ns \n"; 75d99bb014Slntue log << " Average runtime : " << other_average << " ns/op \n"; 76d99bb014Slntue log << " Ops per second : " 77d99bb014Slntue << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n"; 78d99bb014Slntue 79d99bb014Slntue log << "-- Average runtime ratio --\n"; 80d99bb014Slntue log << " Mine / Other's : " << my_average / other_average << " \n"; 81d99bb014Slntue } 82d99bb014Slntue 830cdb0b74SOverMighty static void run_perf(Func myFunc, Func otherFunc, int rounds, 840cdb0b74SOverMighty const char *logFile) { 85d99bb014Slntue std::ofstream log(logFile); 86d99bb014Slntue log << " Performance tests with inputs in denormal range:\n"; 87d99bb014Slntue run_perf_in_range(myFunc, otherFunc, /* startingBit= */ StorageType(0), 88d99bb014Slntue /* endingBit= */ FPBits::max_subnormal().uintval(), 890cdb0b74SOverMighty 1'000'001, rounds, log); 90d99bb014Slntue log << "\n Performance tests with inputs in normal range:\n"; 91d99bb014Slntue run_perf_in_range(myFunc, otherFunc, 92d99bb014Slntue /* startingBit= */ FPBits::min_normal().uintval(), 93d99bb014Slntue /* endingBit= */ FPBits::max_normal().uintval(), 940cdb0b74SOverMighty 1'000'001, rounds, log); 95d99bb014Slntue log << "\n Performance tests with inputs in normal range with exponents " 96d99bb014Slntue "close to each other:\n"; 97*1ace91f9SJob Henandez Lara run_perf_in_range( 98*1ace91f9SJob Henandez Lara myFunc, otherFunc, 99*1ace91f9SJob Henandez Lara /* startingBit= */ FPBits(OutputType(0x1.0p-10)).uintval(), 100*1ace91f9SJob Henandez Lara /* endingBit= */ FPBits(OutputType(0x1.0p+10)).uintval(), 1'000'001, 101*1ace91f9SJob Henandez Lara rounds, log); 102d99bb014Slntue } 103d99bb014Slntue 104d99bb014Slntue static void run_diff(Func myFunc, Func otherFunc, const char *logFile) { 105d99bb014Slntue uint64_t diffCount = 0; 106d99bb014Slntue std::ofstream log(logFile); 107d99bb014Slntue log << " Diff tests with inputs in denormal range:\n"; 108d99bb014Slntue diffCount += run_diff_in_range( 109d99bb014Slntue myFunc, otherFunc, /* startingBit= */ StorageType(0), 110d99bb014Slntue /* endingBit= */ FPBits::max_subnormal().uintval(), 1'000'001, log); 111d99bb014Slntue log << "\n Diff tests with inputs in normal range:\n"; 112d99bb014Slntue diffCount += run_diff_in_range( 113d99bb014Slntue myFunc, otherFunc, 114d99bb014Slntue /* startingBit= */ FPBits::min_normal().uintval(), 115d99bb014Slntue /* endingBit= */ FPBits::max_normal().uintval(), 100'000'001, log); 116d99bb014Slntue log << "\n Diff tests with inputs in normal range with exponents " 117d99bb014Slntue "close to each other:\n"; 118d99bb014Slntue diffCount += run_diff_in_range( 119*1ace91f9SJob Henandez Lara myFunc, otherFunc, 120*1ace91f9SJob Henandez Lara /* startingBit= */ FPBits(OutputType(0x1.0p-10)).uintval(), 121*1ace91f9SJob Henandez Lara /* endingBit= */ FPBits(OutputType(0x1.0p+10)).uintval(), 10'000'001, 122*1ace91f9SJob Henandez Lara log); 123d99bb014Slntue 124d99bb014Slntue log << "Total number of differing results: " << diffCount << '\n'; 125d99bb014Slntue } 126d99bb014Slntue }; 127d99bb014Slntue 128d99bb014Slntue } // namespace testing 1295ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL 130d99bb014Slntue 131*1ace91f9SJob Henandez Lara #define BINARY_OP_SINGLE_OUTPUT_PERF(OutputType, InputType, myFunc, otherFunc, \ 132*1ace91f9SJob Henandez Lara filename) \ 133d99bb014Slntue int main() { \ 134*1ace91f9SJob Henandez Lara LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf< \ 135*1ace91f9SJob Henandez Lara OutputType, InputType>::run_perf(&myFunc, &otherFunc, 1, filename); \ 136d99bb014Slntue return 0; \ 137d99bb014Slntue } 1380cdb0b74SOverMighty 139*1ace91f9SJob Henandez Lara #define BINARY_OP_SINGLE_OUTPUT_PERF_EX(OutputType, InputType, myFunc, \ 140*1ace91f9SJob Henandez Lara otherFunc, rounds, filename) \ 1410cdb0b74SOverMighty { \ 142*1ace91f9SJob Henandez Lara LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf< \ 143*1ace91f9SJob Henandez Lara OutputType, InputType>::run_perf(&myFunc, &otherFunc, rounds, \ 144*1ace91f9SJob Henandez Lara filename); \ 145*1ace91f9SJob Henandez Lara LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf< \ 146*1ace91f9SJob Henandez Lara OutputType, InputType>::run_perf(&myFunc, &otherFunc, rounds, \ 147*1ace91f9SJob Henandez Lara filename); \ 1480cdb0b74SOverMighty } 149