[libc] Add differential quality and perf analysis targets for sinf and cosf.

Infrastructure needed for setting up the diff binaries has been added.
 Along the way, an exhaustive test for sinf and cosf have also been added.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D101276
This commit is contained in:
Siva Chandra Reddy 2021-04-24 06:21:12 +00:00
parent 51b4610743
commit c6aa206b42
16 changed files with 438 additions and 3 deletions

View file

@ -1134,3 +1134,4 @@ add_fp_unittest(
add_subdirectory(generic)
add_subdirectory(exhaustive)
add_subdirectory(differential_testing)

View file

@ -0,0 +1,108 @@
function(add_diff_binary target_name)
cmake_parse_arguments(
"DIFF"
"" # No optional arguments
"SUITE" # Single value arguments
"SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
${ARGN}
)
if(NOT DIFF_SRCS)
message(FATAL_ERROR "'add_diff_binary' target requires a SRCS list of .cpp "
"files.")
endif()
if(NOT DIFF_DEPENDS)
message(FATAL_ERROR "'add_diff_binary' target requires a DEPENDS list of "
"'add_entrypoint_object' targets.")
endif()
get_fq_target_name(${target_name} fq_target_name)
get_fq_deps_list(fq_deps_list ${DIFF_DEPENDS})
get_object_files_for_test(
link_object_files skipped_entrypoints_list ${fq_deps_list})
if(skipped_entrypoints_list)
set(msg "Will not build ${fq_target_name} as it has missing deps: "
"${skipped_entrypoints_list}.")
message(STATUS ${msg})
return()
endif()
add_executable(
${fq_target_name}
EXCLUDE_FROM_ALL
${DIFF_SRCS}
${DIFF_HDRS}
)
target_include_directories(
${fq_target_name}
PRIVATE
${LIBC_SOURCE_DIR}
${LIBC_BUILD_DIR}
${LIBC_BUILD_DIR}/include
)
if(DIFF_COMPILE_OPTIONS)
target_compile_options(
${fq_target_name}
PRIVATE ${DIFF_COMPILE_OPTIONS}
)
endif()
target_link_libraries(
${fq_target_name}
PRIVATE ${link_object_files} libc_test_utils)
set_target_properties(${fq_target_name}
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
add_dependencies(
${fq_target_name}
libc.utils.FPUtil.fputil
${fq_deps_list}
)
endfunction()
add_header_library(
single_input_single_output_diff
HDRS
SingleInputSingleOutputDiff.h
)
add_diff_binary(
sinf_diff
SRCS
sinf_diff.cpp
DEPENDS
.single_input_single_output_diff
libc.src.math.sinf
)
add_diff_binary(
sinf_perf
SRCS
sinf_perf.cpp
DEPENDS
.single_input_single_output_diff
libc.src.math.sinf
COMPILE_OPTIONS
-fno-builtin
)
add_diff_binary(
cosf_diff
SRCS
cosf_diff.cpp
DEPENDS
.single_input_single_output_diff
libc.src.math.cosf
)
add_diff_binary(
cosf_perf
SRCS
cosf_perf.cpp
DEPENDS
.single_input_single_output_diff
libc.src.math.cosf
COMPILE_OPTIONS
-fno-builtin
)

View file

@ -0,0 +1,89 @@
//===-- 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 "utils/FPUtil/FPBits.h"
#include "utils/testutils/StreamWrapper.h"
#include "utils/testutils/Timer.h"
namespace __llvm_libc {
namespace testing {
template <typename T> class SingleInputSingleOutputDiff {
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);
static void runDiff(Func myFunc, Func otherFunc, const char *logFile) {
UIntType diffCount = 0;
testutils::OutputFileStream log(logFile);
log << "Starting diff for values from 0 to " << UIntMax << '\n'
<< "Only differing results will be logged.\n\n";
for (UIntType bits = 0;; ++bits) {
T x = T(FPBits(bits));
T myResult = myFunc(x);
T otherResult = otherFunc(x);
UIntType myBits = FPBits(myResult).uintval();
UIntType otherBits = FPBits(otherResult).uintval();
if (myBits != otherBits) {
++diffCount;
log << " Input: " << bits << " (" << x << ")\n"
<< " My result: " << myBits << " (" << myResult << ")\n"
<< "Other result: " << otherBits << " (" << otherResult << ")\n"
<< '\n';
}
if (bits == UIntMax)
break;
}
log << "Total number of differing results: " << diffCount << '\n';
}
static void runPerf(Func myFunc, Func otherFunc, const char *logFile) {
auto runner = [](Func func) {
volatile T result;
for (UIntType bits = 0;; ++bits) {
T x = T(FPBits(bits));
result = func(x);
if (bits == UIntMax)
break;
}
};
testutils::OutputFileStream log(logFile);
Timer timer;
timer.start();
runner(myFunc);
timer.stop();
log << " Run time of my function: " << timer.nanoseconds() << " ns \n";
timer.start();
runner(otherFunc);
timer.stop();
log << "Run time of other function: " << timer.nanoseconds() << " ns \n";
}
};
} // namespace testing
} // namespace __llvm_libc
#define SINGLE_INPUT_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename) \
int main() { \
__llvm_libc::testing::SingleInputSingleOutputDiff<T>::runDiff( \
&myFunc, &otherFunc, filename); \
return 0; \
}
#define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \
int main() { \
__llvm_libc::testing::SingleInputSingleOutputDiff<T>::runPerf( \
&myFunc, &otherFunc, filename); \
return 0; \
}

View file

@ -0,0 +1,16 @@
//===-- Differential test for cosf ----------------------------------------===//
//
// 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 "SingleInputSingleOutputDiff.h"
#include "src/math/cosf.h"
#include <math.h>
SINGLE_INPUT_SINGLE_OUTPUT_DIFF(float, __llvm_libc::cosf, ::cosf,
"cosf_diff.log")

View file

@ -0,0 +1,16 @@
//===-- Differential test for cosf ----------------------------------------===//
//
// 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 "SingleInputSingleOutputDiff.h"
#include "src/math/cosf.h"
#include <math.h>
SINGLE_INPUT_SINGLE_OUTPUT_PERF(float, __llvm_libc::cosf, ::cosf,
"cosf_perf.log")

View file

@ -0,0 +1,16 @@
//===-- Differential test for sinf ----------------------------------------===//
//
// 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 "SingleInputSingleOutputDiff.h"
#include "src/math/sinf.h"
#include <math.h>
SINGLE_INPUT_SINGLE_OUTPUT_DIFF(float, __llvm_libc::sinf, ::sinf,
"sinf_diff.log")

View file

@ -0,0 +1,16 @@
//===-- Differential test for sinf ----------------------------------------===//
//
// 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 "SingleInputSingleOutputDiff.h"
#include "src/math/sinf.h"
#include <math.h>
SINGLE_INPUT_SINGLE_OUTPUT_PERF(float, __llvm_libc::sinf, ::sinf,
"sinf_perf.log")

View file

@ -12,3 +12,29 @@ add_fp_unittest(
libc.src.math.sqrtf
libc.utils.FPUtil.fputil
)
add_fp_unittest(
sinf_test
NEED_MPFR
SUITE
libc_math_exhaustive_tests
SRCS
sinf_test.cpp
DEPENDS
libc.include.math
libc.src.math.sinf
libc.utils.FPUtil.fputil
)
add_fp_unittest(
cosf_test
NEED_MPFR
SUITE
libc_math_exhaustive_tests
SRCS
cosf_test.cpp
DEPENDS
libc.include.math
libc.src.math.cosf
libc.utils.FPUtil.fputil
)

View file

@ -0,0 +1,26 @@
//===-- Exhaustive test for cosf ------------------------------------------===//
//
// 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/math/cosf.h"
#include "utils/FPUtil/FPBits.h"
#include "utils/FPUtil/TestHelpers.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include <math.h>
using FPBits = __llvm_libc::fputil::FPBits<float>;
namespace mpfr = __llvm_libc::testing::mpfr;
TEST(LlvmLibccosffExhaustiveTest, AllValues) {
uint32_t bits = 0;
do {
FPBits xbits(bits);
float x = float(xbits);
ASSERT_MPFR_MATCH(mpfr::Operation::Cos, x, __llvm_libc::cosf(x), 1.0);
} while (bits++ < 0xffff'ffffU);
}

View file

@ -0,0 +1,26 @@
//===-- Exhaustive test for sinf ------------------------------------------===//
//
// 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/math/sinf.h"
#include "utils/FPUtil/FPBits.h"
#include "utils/FPUtil/TestHelpers.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include <math.h>
using FPBits = __llvm_libc::fputil::FPBits<float>;
namespace mpfr = __llvm_libc::testing::mpfr;
TEST(LlvmLibcsinffExhaustiveTest, AllValues) {
uint32_t bits = 0;
do {
FPBits xbits(bits);
float x = float(xbits);
ASSERT_MPFR_MATCH(mpfr::Operation::Sin, x, __llvm_libc::sinf(x), 1.0);
} while (bits++ < 0xffff'ffffU);
}

View file

@ -19,8 +19,8 @@ namespace mpfr = __llvm_libc::testing::mpfr;
TEST(LlvmLibcSqrtfExhaustiveTest, AllValues) {
uint32_t bits = 0;
do {
FPBits x(bits);
ASSERT_MPFR_MATCH(mpfr::Operation::Sqrt, float(x), __llvm_libc::sqrtf(x),
0.5);
FPBits xbits(bits);
float x = float(xbits);
ASSERT_MPFR_MATCH(mpfr::Operation::Sqrt, x, __llvm_libc::sqrtf(x), 0.5);
} while (bits++ < 0xffff'ffffU);
}

View file

@ -13,4 +13,6 @@ add_llvm_library(
ExecuteFunction.h
${FDReaderFile}
FDReader.h
Timer.h
Timer.cpp
)

View file

@ -8,6 +8,7 @@
#include "StreamWrapper.h"
#include <cassert>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
@ -21,6 +22,7 @@ template <typename T> StreamWrapper &StreamWrapper::operator<<(T t) {
assert(OS);
std::ostream &Stream = *reinterpret_cast<std::ostream *>(OS);
Stream << t;
Stream.flush();
return *this;
}
@ -43,6 +45,15 @@ template StreamWrapper &
StreamWrapper::operator<<<unsigned long long>(unsigned long long t);
template StreamWrapper &StreamWrapper::operator<<<bool>(bool t);
template StreamWrapper &StreamWrapper::operator<<<std::string>(std::string t);
template StreamWrapper &StreamWrapper::operator<<<float>(float t);
template StreamWrapper &StreamWrapper::operator<<<double>(double t);
OutputFileStream::OutputFileStream(const char *FN)
: StreamWrapper(new std::ofstream(FN)) {}
OutputFileStream::~OutputFileStream() {
delete reinterpret_cast<std::ofstream *>(OS);
}
} // namespace testutils
} // namespace __llvm_libc

View file

@ -16,6 +16,7 @@ namespace testutils {
// standard headers so we must provide streams through indirection to not
// expose the system libc headers.
class StreamWrapper {
protected:
void *OS;
public:
@ -26,6 +27,12 @@ public:
StreamWrapper outs();
class OutputFileStream : public StreamWrapper {
public:
explicit OutputFileStream(const char *FN);
~OutputFileStream();
};
} // namespace testutils
} // namespace __llvm_libc

View file

@ -0,0 +1,42 @@
//===-- Timer.cpp --------------------------------------------------------===//
//
// 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 "Timer.h"
#include <chrono>
#include <fstream>
namespace __llvm_libc {
namespace testing {
struct TimerImplementation {
std::chrono::high_resolution_clock::time_point Start;
std::chrono::high_resolution_clock::time_point End;
};
Timer::Timer() : Impl(new TimerImplementation) {}
Timer::~Timer() { delete reinterpret_cast<TimerImplementation *>(Impl); }
void Timer::start() {
auto T = reinterpret_cast<TimerImplementation *>(Impl);
T->Start = std::chrono::high_resolution_clock::now();
}
void Timer::stop() {
auto T = reinterpret_cast<TimerImplementation *>(Impl);
T->End = std::chrono::high_resolution_clock::now();
}
uint64_t Timer::nanoseconds() const {
auto T = reinterpret_cast<TimerImplementation *>(Impl);
return std::chrono::nanoseconds(T->End - T->Start).count();
}
} // namespace testing
} // namespace __llvm_libc

View file

@ -0,0 +1,33 @@
//===-- Timer.h -------------------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_UTILS_TESTUTILS_TIMER_H
#define LLVM_LIBC_UTILS_TESTUTILS_TIMER_H
#include <stdint.h>
namespace __llvm_libc {
namespace testing {
class Timer {
void *Impl;
public:
Timer();
~Timer();
void start();
void stop();
uint64_t nanoseconds() const;
};
} // namespace testing
} // namespace __llvm_libc
#endif // LLVM_LIBC_UTILS_TESTUTILS_TIMER_H