blob: ccab38e5287909b45f5d5d4964eab6daa1388162 [file] [log] [blame]
//===-- Common utility class for differential analysis --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/__support/FPUtil/FPBits.h"
#include "test/src/math/differential_testing/Timer.h"
#include <fstream>
namespace __llvm_libc {
namespace testing {
template <typename T> class BinaryOpSingleOutputDiff {
using FPBits = fputil::FPBits<T>;
using UIntType = typename FPBits::UIntType;
static constexpr UIntType MSBIT = UIntType(1) << (8 * sizeof(UIntType) - 1);
static constexpr UIntType UINTMAX = (MSBIT - 1) + MSBIT;
public:
typedef T Func(T, T);
static uint64_t run_diff_in_range(Func myFunc, Func otherFunc,
UIntType startingBit, UIntType endingBit,
UIntType N, std::ofstream &log) {
uint64_t result = 0;
if (endingBit < startingBit) {
return result;
}
UIntType step = (endingBit - startingBit) / N;
for (UIntType bitsX = startingBit, bitsY = endingBit;;
bitsX += step, bitsY -= step) {
T x = T(FPBits(bitsX));
T y = T(FPBits(bitsY));
FPBits myBits = FPBits(myFunc(x, y));
FPBits otherBits = FPBits(otherFunc(x, y));
if (myBits.uintval() != otherBits.uintval()) {
result++;
log << " Input: " << bitsX << ", " << bitsY << " (" << x << ", "
<< y << ")\n"
<< " My result: " << myBits.uintval() << " (" << myBits.get_val()
<< ")\n"
<< "Other result: " << otherBits.uintval() << " ("
<< otherBits.get_val() << ")\n"
<< '\n';
}
if (endingBit - bitsX < step) {
break;
}
}
return result;
}
static void run_perf_in_range(Func myFunc, Func otherFunc,
UIntType startingBit, UIntType endingBit,
UIntType N, std::ofstream &log) {
auto runner = [=](Func func) {
volatile T result;
if (endingBit < startingBit) {
return;
}
UIntType step = (endingBit - startingBit) / N;
for (UIntType bitsX = startingBit, bitsY = endingBit;;
bitsX += step, bitsY -= step) {
T x = T(FPBits(bitsX));
T y = T(FPBits(bitsY));
result = func(x, y);
if (endingBit - bitsX < step) {
break;
}
}
};
Timer timer;
timer.start();
runner(myFunc);
timer.stop();
double my_average = static_cast<double>(timer.nanoseconds()) / N;
log << "-- My function --\n";
log << " Total time : " << timer.nanoseconds() << " ns \n";
log << " Average runtime : " << my_average << " ns/op \n";
log << " Ops per second : "
<< static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n";
timer.start();
runner(otherFunc);
timer.stop();
double other_average = static_cast<double>(timer.nanoseconds()) / N;
log << "-- Other function --\n";
log << " Total time : " << timer.nanoseconds() << " ns \n";
log << " Average runtime : " << other_average << " ns/op \n";
log << " Ops per second : "
<< static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n";
log << "-- Average runtime ratio --\n";
log << " Mine / Other's : " << my_average / other_average << " \n";
}
static void run_perf(Func myFunc, Func otherFunc, const char *logFile) {
std::ofstream log(logFile);
log << " Performance tests with inputs in denormal range:\n";
run_perf_in_range(myFunc, otherFunc, /* startingBit= */ UIntType(0),
/* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log);
log << "\n Performance tests with inputs in normal range:\n";
run_perf_in_range(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
/* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log);
log << "\n Performance tests with inputs in normal range with exponents "
"close to each other:\n";
run_perf_in_range(
myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
/* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
}
static void run_diff(Func myFunc, Func otherFunc, const char *logFile) {
uint64_t diffCount = 0;
std::ofstream log(logFile);
log << " Diff tests with inputs in denormal range:\n";
diffCount += run_diff_in_range(
myFunc, otherFunc, /* startingBit= */ UIntType(0),
/* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log);
log << "\n Diff tests with inputs in normal range:\n";
diffCount += run_diff_in_range(
myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
/* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log);
log << "\n Diff tests with inputs in normal range with exponents "
"close to each other:\n";
diffCount += run_diff_in_range(
myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
/* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
log << "Total number of differing results: " << diffCount << '\n';
}
};
} // namespace testing
} // namespace __llvm_libc
#define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename) \
int main() { \
__llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_diff( \
&myFunc, &otherFunc, filename); \
return 0; \
}
#define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \
int main() { \
__llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_perf( \
&myFunc, &otherFunc, filename); \
return 0; \
}