brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · 3cc6b24 Raw
160 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "src/__support/CPP/algorithm.h"10#include "src/__support/FPUtil/FPBits.h"11#include "src/__support/macros/config.h"12#include "test/src/math/performance_testing/Timer.h"13 14#include <cstddef>15#include <fstream>16 17namespace LIBC_NAMESPACE_DECL {18namespace testing {19template <typename OutputType, typename InputType> class PerfTest {20  using FPBits = fputil::FPBits<OutputType>;21  using StorageType = typename FPBits::StorageType;22  static constexpr StorageType U_INT_MAX =23      cpp::numeric_limits<StorageType>::max();24 25public:26  using BinaryFuncPtr = OutputType (*)(InputType, InputType);27  using UnaryFuncPtr = OutputType (*)(InputType);28 29  template <bool binary, typename Func>30  static void run_perf_in_range(Func FuncA, Func FuncB, StorageType startingBit,31                                StorageType endingBit, size_t N, size_t rounds,32                                const char *name_a, const char *name_b,33                                std::ofstream &log) {34    if (sizeof(StorageType) <= sizeof(size_t))35      N = cpp::min(N, static_cast<size_t>(endingBit - startingBit));36 37    auto runner = [=](Func func) {38      [[maybe_unused]] volatile OutputType result;39      if (endingBit < startingBit) {40        return;41      }42 43      StorageType step = (endingBit - startingBit) / N;44      if (step == 0)45        step = 1;46      for (size_t i = 0; i < rounds; i++) {47        for (StorageType bits_x = startingBit, bits_y = endingBit;;48             bits_x += step, bits_y -= step) {49          InputType x = FPBits(bits_x).get_val();50          if constexpr (binary) {51            InputType y = FPBits(bits_y).get_val();52            result = func(x, y);53          } else {54            result = func(x);55          }56          if (endingBit - bits_x < step) {57            break;58          }59        }60      }61    };62 63    Timer timer;64    timer.start();65    runner(FuncA);66    timer.stop();67 68    double a_average = static_cast<double>(timer.nanoseconds()) / N / rounds;69    log << "-- Function A: " << name_a << " --\n";70    log << "     Total time      : " << timer.nanoseconds() << " ns \n";71    log << "     Average runtime : " << a_average << " ns/op \n";72    log << "     Ops per second  : "73        << static_cast<uint64_t>(1'000'000'000.0 / a_average) << " op/s \n";74 75    timer.start();76    runner(FuncB);77    timer.stop();78 79    double b_average = static_cast<double>(timer.nanoseconds()) / N / rounds;80    log << "-- Function B: " << name_b << " --\n";81    log << "     Total time      : " << timer.nanoseconds() << " ns \n";82    log << "     Average runtime : " << b_average << " ns/op \n";83    log << "     Ops per second  : "84        << static_cast<uint64_t>(1'000'000'000.0 / b_average) << " op/s \n";85 86    log << "-- Average ops per second ratio --\n";87    log << "     A / B  : " << b_average / a_average << " \n";88  }89 90  template <bool binary, typename Func>91  static void run_perf(Func FuncA, Func FuncB, int rounds, const char *name_a,92                       const char *name_b, const char *logFile) {93    std::ofstream log(logFile);94    log << " Performance tests with inputs in denormal range:\n";95    run_perf_in_range<binary>(96        FuncA, FuncB, /* startingBit= */ StorageType(0),97        /* endingBit= */ FPBits::max_subnormal().uintval(), 1'000'001, rounds,98        name_a, name_b, log);99    log << "\n Performance tests with inputs in normal range:\n";100    run_perf_in_range<binary>(FuncA, FuncB,101                              /* startingBit= */ FPBits::min_normal().uintval(),102                              /* endingBit= */ FPBits::max_normal().uintval(),103                              1'000'001, rounds, name_a, name_b, log);104    log << "\n Performance tests with inputs in normal range with exponents "105           "close to each other:\n";106    run_perf_in_range<binary>(107        FuncA, FuncB,108        /* startingBit= */ FPBits(OutputType(0x1.0p-10)).uintval(),109        /* endingBit= */ FPBits(OutputType(0x1.0p+10)).uintval(), 1'000'001,110        rounds, name_a, name_b, log);111  }112};113 114} // namespace testing115} // namespace LIBC_NAMESPACE_DECL116 117#define BINARY_INPUT_SINGLE_OUTPUT_PERF(OutputType, InputType, FuncA, FuncB,   \118                                        filename)                              \119  {                                                                            \120    using TargetFuncPtr =                                                      \121        typename LIBC_NAMESPACE::testing::PerfTest<OutputType,                 \122                                                   InputType>::BinaryFuncPtr;  \123    LIBC_NAMESPACE::testing::PerfTest<OutputType, InputType>::run_perf<true>(  \124        static_cast<TargetFuncPtr>(&FuncA),                                    \125        static_cast<TargetFuncPtr>(&FuncB), 1, #FuncA, #FuncB, filename);      \126    return 0;                                                                  \127  }128 129#define BINARY_INPUT_SINGLE_OUTPUT_PERF_EX(OutputType, InputType, FuncA,       \130                                           FuncB, rounds, filename)            \131  {                                                                            \132    using TargetFuncPtr =                                                      \133        typename LIBC_NAMESPACE::testing::PerfTest<OutputType,                 \134                                                   InputType>::BinaryFuncPtr;  \135    LIBC_NAMESPACE::testing::PerfTest<OutputType, InputType>::run_perf<true>(  \136        static_cast<TargetFuncPtr>(&FuncA),                                    \137        static_cast<TargetFuncPtr>(&FuncB), rounds, #FuncA, #FuncB, filename); \138    return 0;                                                                  \139  }140 141#define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, FuncA, FuncB, filename)             \142  {                                                                            \143    using TargetFuncPtr =                                                      \144        typename LIBC_NAMESPACE::testing::PerfTest<T, T>::UnaryFuncPtr;        \145    LIBC_NAMESPACE::testing::PerfTest<T, T>::run_perf<false>(                  \146        static_cast<TargetFuncPtr>(&FuncA),                                    \147        static_cast<TargetFuncPtr>(&FuncB), 1, #FuncA, #FuncB, filename);      \148    return 0;                                                                  \149  }150 151#define SINGLE_INPUT_SINGLE_OUTPUT_PERF_EX(T, FuncA, FuncB, rounds, filename)  \152  {                                                                            \153    using TargetFuncPtr =                                                      \154        typename LIBC_NAMESPACE::testing::PerfTest<T, T>::UnaryFuncPtr;        \155    LIBC_NAMESPACE::testing::PerfTest<T, T>::run_perf<false>(                  \156        static_cast<TargetFuncPtr>(&FuncA),                                    \157        static_cast<TargetFuncPtr>(&FuncB), rounds, #FuncA, #FuncB, filename); \158    return 0;                                                                  \159  }160