112779eddSTue Ly //===-- Common utility class for differential analysis --------------------===// 212779eddSTue Ly // 312779eddSTue Ly // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 412779eddSTue Ly // See https://llvm.org/LICENSE.txt for license information. 512779eddSTue Ly // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 612779eddSTue Ly // 712779eddSTue Ly //===----------------------------------------------------------------------===// 812779eddSTue Ly 912779eddSTue Ly #include "src/__support/FPUtil/FPBits.h" 1012779eddSTue Ly #include "utils/testutils/StreamWrapper.h" 1112779eddSTue Ly #include "utils/testutils/Timer.h" 1212779eddSTue Ly 1312779eddSTue Ly namespace __llvm_libc { 1412779eddSTue Ly namespace testing { 1512779eddSTue Ly 1612779eddSTue Ly template <typename T> class BinaryOpSingleOutputDiff { 1712779eddSTue Ly using FPBits = fputil::FPBits<T>; 1812779eddSTue Ly using UIntType = typename FPBits::UIntType; 1912779eddSTue Ly static constexpr UIntType MSBIT = UIntType(1) << (8 * sizeof(UIntType) - 1); 2012779eddSTue Ly static constexpr UIntType UINTMAX = (MSBIT - 1) + MSBIT; 2112779eddSTue Ly 2212779eddSTue Ly public: 2312779eddSTue Ly typedef T Func(T, T); 2412779eddSTue Ly run_diff_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)25*76b57ef8SKirill Okhotnikov static uint64_t run_diff_in_range(Func myFunc, Func otherFunc, 26*76b57ef8SKirill Okhotnikov UIntType startingBit, UIntType endingBit, 27*76b57ef8SKirill Okhotnikov UIntType N, 28*76b57ef8SKirill Okhotnikov testutils::OutputFileStream &log) { 29*76b57ef8SKirill Okhotnikov uint64_t result = 0; 30*76b57ef8SKirill Okhotnikov if (endingBit < startingBit) { 31*76b57ef8SKirill Okhotnikov return result; 32*76b57ef8SKirill Okhotnikov } 33*76b57ef8SKirill Okhotnikov 34*76b57ef8SKirill Okhotnikov UIntType step = (endingBit - startingBit) / N; 35*76b57ef8SKirill Okhotnikov for (UIntType bitsX = startingBit, bitsY = endingBit;; 36*76b57ef8SKirill Okhotnikov bitsX += step, bitsY -= step) { 37*76b57ef8SKirill Okhotnikov T x = T(FPBits(bitsX)); 38*76b57ef8SKirill Okhotnikov T y = T(FPBits(bitsY)); 39*76b57ef8SKirill Okhotnikov FPBits myBits = FPBits(myFunc(x, y)); 40*76b57ef8SKirill Okhotnikov FPBits otherBits = FPBits(otherFunc(x, y)); 41*76b57ef8SKirill Okhotnikov if (myBits.uintval() != otherBits.uintval()) { 42*76b57ef8SKirill Okhotnikov result++; 43*76b57ef8SKirill Okhotnikov log << " Input: " << bitsX << ", " << bitsY << " (" << x << ", " 44*76b57ef8SKirill Okhotnikov << y << ")\n" 45*76b57ef8SKirill Okhotnikov << " My result: " << myBits.uintval() << " (" << myBits.get_val() 46*76b57ef8SKirill Okhotnikov << ")\n" 47*76b57ef8SKirill Okhotnikov << "Other result: " << otherBits.uintval() << " (" 48*76b57ef8SKirill Okhotnikov << otherBits.get_val() << ")\n" 49*76b57ef8SKirill Okhotnikov << '\n'; 50*76b57ef8SKirill Okhotnikov } 51*76b57ef8SKirill Okhotnikov 52*76b57ef8SKirill Okhotnikov if (endingBit - bitsX < step) { 53*76b57ef8SKirill Okhotnikov break; 54*76b57ef8SKirill Okhotnikov } 55*76b57ef8SKirill Okhotnikov } 56*76b57ef8SKirill Okhotnikov return result; 57*76b57ef8SKirill Okhotnikov } 58*76b57ef8SKirill Okhotnikov run_perf_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)5912779eddSTue Ly static void run_perf_in_range(Func myFunc, Func otherFunc, 6012779eddSTue Ly UIntType startingBit, UIntType endingBit, 6112779eddSTue Ly UIntType N, testutils::OutputFileStream &log) { 6212779eddSTue Ly auto runner = [=](Func func) { 6312779eddSTue Ly volatile T result; 6412779eddSTue Ly if (endingBit < startingBit) { 6512779eddSTue Ly return; 6612779eddSTue Ly } 6712779eddSTue Ly 6812779eddSTue Ly UIntType step = (endingBit - startingBit) / N; 6912779eddSTue Ly for (UIntType bitsX = startingBit, bitsY = endingBit;; 7012779eddSTue Ly bitsX += step, bitsY -= step) { 7112779eddSTue Ly T x = T(FPBits(bitsX)); 7212779eddSTue Ly T y = T(FPBits(bitsY)); 7312779eddSTue Ly result = func(x, y); 7412779eddSTue Ly if (endingBit - bitsX < step) { 7512779eddSTue Ly break; 7612779eddSTue Ly } 7712779eddSTue Ly } 7812779eddSTue Ly }; 7912779eddSTue Ly 8012779eddSTue Ly Timer timer; 8112779eddSTue Ly timer.start(); 8212779eddSTue Ly runner(myFunc); 8312779eddSTue Ly timer.stop(); 8412779eddSTue Ly 8512779eddSTue Ly double my_average = static_cast<double>(timer.nanoseconds()) / N; 8612779eddSTue Ly log << "-- My function --\n"; 8712779eddSTue Ly log << " Total time : " << timer.nanoseconds() << " ns \n"; 8812779eddSTue Ly log << " Average runtime : " << my_average << " ns/op \n"; 8912779eddSTue Ly log << " Ops per second : " 9012779eddSTue Ly << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n"; 9112779eddSTue Ly 9212779eddSTue Ly timer.start(); 9312779eddSTue Ly runner(otherFunc); 9412779eddSTue Ly timer.stop(); 9512779eddSTue Ly 9612779eddSTue Ly double other_average = static_cast<double>(timer.nanoseconds()) / N; 9712779eddSTue Ly log << "-- Other function --\n"; 9812779eddSTue Ly log << " Total time : " << timer.nanoseconds() << " ns \n"; 9912779eddSTue Ly log << " Average runtime : " << other_average << " ns/op \n"; 10012779eddSTue Ly log << " Ops per second : " 10112779eddSTue Ly << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n"; 10212779eddSTue Ly 10312779eddSTue Ly log << "-- Average runtime ratio --\n"; 10412779eddSTue Ly log << " Mine / Other's : " << my_average / other_average << " \n"; 10512779eddSTue Ly } 10612779eddSTue Ly run_perf(Func myFunc,Func otherFunc,const char * logFile)10712779eddSTue Ly static void run_perf(Func myFunc, Func otherFunc, const char *logFile) { 10812779eddSTue Ly testutils::OutputFileStream log(logFile); 10912779eddSTue Ly log << " Performance tests with inputs in denormal range:\n"; 11012779eddSTue Ly run_perf_in_range(myFunc, otherFunc, /* startingBit= */ UIntType(0), 11112779eddSTue Ly /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log); 11212779eddSTue Ly log << "\n Performance tests with inputs in normal range:\n"; 11312779eddSTue Ly run_perf_in_range(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL, 11412779eddSTue Ly /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log); 115f1ec99f9STue Ly log << "\n Performance tests with inputs in normal range with exponents " 116f1ec99f9STue Ly "close to each other:\n"; 117f1ec99f9STue Ly run_perf_in_range( 118f1ec99f9STue Ly myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(), 119f1ec99f9STue Ly /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log); 12012779eddSTue Ly } 121*76b57ef8SKirill Okhotnikov run_diff(Func myFunc,Func otherFunc,const char * logFile)122*76b57ef8SKirill Okhotnikov static void run_diff(Func myFunc, Func otherFunc, const char *logFile) { 123*76b57ef8SKirill Okhotnikov uint64_t diffCount = 0; 124*76b57ef8SKirill Okhotnikov testutils::OutputFileStream log(logFile); 125*76b57ef8SKirill Okhotnikov log << " Diff tests with inputs in denormal range:\n"; 126*76b57ef8SKirill Okhotnikov diffCount += run_diff_in_range( 127*76b57ef8SKirill Okhotnikov myFunc, otherFunc, /* startingBit= */ UIntType(0), 128*76b57ef8SKirill Okhotnikov /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log); 129*76b57ef8SKirill Okhotnikov log << "\n Diff tests with inputs in normal range:\n"; 130*76b57ef8SKirill Okhotnikov diffCount += run_diff_in_range( 131*76b57ef8SKirill Okhotnikov myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL, 132*76b57ef8SKirill Okhotnikov /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log); 133*76b57ef8SKirill Okhotnikov log << "\n Diff tests with inputs in normal range with exponents " 134*76b57ef8SKirill Okhotnikov "close to each other:\n"; 135*76b57ef8SKirill Okhotnikov diffCount += run_diff_in_range( 136*76b57ef8SKirill Okhotnikov myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(), 137*76b57ef8SKirill Okhotnikov /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log); 138*76b57ef8SKirill Okhotnikov 139*76b57ef8SKirill Okhotnikov log << "Total number of differing results: " << diffCount << '\n'; 140*76b57ef8SKirill Okhotnikov } 14112779eddSTue Ly }; 14212779eddSTue Ly 14312779eddSTue Ly } // namespace testing 14412779eddSTue Ly } // namespace __llvm_libc 14512779eddSTue Ly 14612779eddSTue Ly #define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename) \ 14712779eddSTue Ly int main() { \ 14812779eddSTue Ly __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_diff( \ 14912779eddSTue Ly &myFunc, &otherFunc, filename); \ 15012779eddSTue Ly return 0; \ 15112779eddSTue Ly } 15212779eddSTue Ly 15312779eddSTue Ly #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \ 15412779eddSTue Ly int main() { \ 15512779eddSTue Ly __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_perf( \ 15612779eddSTue Ly &myFunc, &otherFunc, filename); \ 15712779eddSTue Ly return 0; \ 15812779eddSTue Ly } 159