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 runPerfInRange(Func myFunc,Func otherFunc,StorageType startingBit,StorageType endingBit,std::ofstream & log)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 runPerf(Func myFunc,Func otherFunc,const char * logFile)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= */ StorageType(0), 74 /* endingBit= */ FPBits::max_subnormal().uintval(), log); 75 log << "\n Performance tests with inputs in normal range:\n"; 76 runPerfInRange(myFunc, otherFunc, 77 /* startingBit= */ FPBits::min_normal().uintval(), 78 /* 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