[libc-commits] [libc] 8cd8127 - [libc] Add multithreading support for exhaustive testing and MPFRUtils.
Tue Ly via libc-commits
libc-commits at lists.llvm.org
Thu Jan 13 10:46:30 PST 2022
Author: Tue Ly
Date: 2022-01-13T13:46:14-05:00
New Revision: 8cd81274ff618d9d44d1089d3af0c21a76e719ad
URL: https://github.com/llvm/llvm-project/commit/8cd81274ff618d9d44d1089d3af0c21a76e719ad
DIFF: https://github.com/llvm/llvm-project/commit/8cd81274ff618d9d44d1089d3af0c21a76e719ad.diff
LOG: [libc] Add multithreading support for exhaustive testing and MPFRUtils.
Add threading support for exhaustive testing and MPFRUtils.
Reviewed By: sivachandra
Differential Revision: https://reviews.llvm.org/D117028
Added:
libc/test/src/math/exhaustive/exhaustive_test.cpp
libc/test/src/math/exhaustive/exhaustive_test.h
Modified:
libc/cmake/modules/LLVMLibCTestRules.cmake
libc/test/src/math/exhaustive/CMakeLists.txt
libc/test/src/math/exhaustive/logf_test.cpp
libc/utils/MPFRWrapper/MPFRUtils.cpp
libc/utils/UnitTest/FPMatcher.cpp
libc/utils/UnitTest/FPMatcher.h
Removed:
################################################################################
diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index ba06ccfef536a..285119ff79c0d 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -63,6 +63,7 @@ endfunction(get_object_files_for_test)
# HDRS <list of .h files for the test>
# DEPENDS <list of dependencies>
# COMPILE_OPTIONS <list of special compile options for this target>
+# LINK_OPTIONS <list of special linking options for this target>
# )
function(add_libc_unittest target_name)
if(NOT LLVM_INCLUDE_TESTS)
@@ -71,9 +72,9 @@ function(add_libc_unittest target_name)
cmake_parse_arguments(
"LIBC_UNITTEST"
- "" # No optional arguments
+ "NO_RUN_POSTBUILD" # Optional arguments
"SUITE" # Single value arguments
- "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
+ "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_OPTIONS" # Multi-value arguments
"NO_LIBC_UNITTEST_TEST_MAIN"
${ARGN}
)
@@ -140,6 +141,12 @@ function(add_libc_unittest target_name)
endif()
target_link_libraries(${fq_target_name} PRIVATE ${link_object_files})
+ if(LIBC_UNITTEST_LINK_OPTIONS)
+ target_link_options(
+ ${fq_target_name}
+ PRIVATE ${LIBC_UNITTEST_LINK_OPTIONS}
+ )
+ endif()
set_target_properties(${fq_target_name}
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@@ -155,11 +162,14 @@ function(add_libc_unittest target_name)
target_link_libraries(${fq_target_name} PRIVATE LibcUnitTest LibcUnitTestMain libc_test_utils)
endif()
- add_custom_command(
- TARGET ${fq_target_name}
- POST_BUILD
- COMMAND $<TARGET_FILE:${fq_target_name}>
- )
+ if(NOT LIBC_UNITTEST_NO_RUN_POSTBUILD)
+ add_custom_command(
+ TARGET ${fq_target_name}
+ POST_BUILD
+ COMMAND $<TARGET_FILE:${fq_target_name}>
+ )
+ endif()
+
if(LIBC_UNITTEST_SUITE)
add_dependencies(
${LIBC_UNITTEST_SUITE}
diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index 51e659114e1d7..1c1a411160501 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -1,5 +1,15 @@
add_libc_exhaustive_testsuite(libc_math_exhaustive_tests)
+add_object_library(
+ exhaustive_test
+ HDRS
+ exhaustive_test.h
+ SRCS
+ exhaustive_test.cpp
+ DEPENDS
+ libc.src.__support.CPP.standalone_cpp
+)
+
add_fp_unittest(
sqrtf_test
NEED_MPFR
@@ -54,13 +64,17 @@ add_fp_unittest(
add_fp_unittest(
logf_test
+ NO_RUN_POSTBUILD
NEED_MPFR
SUITE
libc_math_exhaustive_tests
SRCS
logf_test.cpp
DEPENDS
+ .exhaustive_test
libc.include.math
libc.src.math.logf
libc.src.__support.FPUtil.fputil
+ LINK_OPTIONS
+ -lpthread
)
diff --git a/libc/test/src/math/exhaustive/exhaustive_test.cpp b/libc/test/src/math/exhaustive/exhaustive_test.cpp
new file mode 100644
index 0000000000000..fe07467a2ec6b
--- /dev/null
+++ b/libc/test/src/math/exhaustive/exhaustive_test.cpp
@@ -0,0 +1,58 @@
+//===-- Exhaustive test template for math functions -------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <fenv.h>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "exhaustive_test.h"
+#include "utils/UnitTest/Test.h"
+
+template <typename T>
+void LlvmLibcExhaustiveTest<T>::test_full_range(T start, T stop, int nthreads,
+ mpfr::RoundingMode rounding) {
+ std::vector<std::thread> thread_list(nthreads);
+ T increment = (stop - start - 1) / nthreads + 1;
+ T begin = start;
+ T end = start + increment - 1;
+ for (int i = 0; i < nthreads; ++i) {
+ thread_list.emplace_back([this, begin, end, rounding]() {
+ std::stringstream msg;
+ msg << "-- Testing from " << begin << " to " << end << " [0x" << std::hex
+ << begin << ", 0x" << end << ") ..." << std::endl;
+ std::cout << msg.str();
+ msg.str("");
+
+ check(begin, end, rounding);
+
+ msg << "** Finished testing from " << std::dec << begin << " to " << end
+ << " [0x" << std::hex << begin << ", 0x" << end << ")" << std::endl;
+ std::cout << msg.str();
+ });
+ begin += increment;
+ end += increment;
+ if (end > stop)
+ end = stop;
+ }
+ for (auto &thread : thread_list) {
+ if (thread.joinable()) {
+ thread.join();
+ }
+ }
+}
+
+template void
+LlvmLibcExhaustiveTest<uint32_t>::test_full_range(uint32_t, uint32_t, int,
+ mpfr::RoundingMode);
+template void
+LlvmLibcExhaustiveTest<uint64_t>::test_full_range(uint64_t, uint64_t, int,
+ mpfr::RoundingMode);
diff --git a/libc/test/src/math/exhaustive/exhaustive_test.h b/libc/test/src/math/exhaustive/exhaustive_test.h
new file mode 100644
index 0000000000000..0782da7a203bd
--- /dev/null
+++ b/libc/test/src/math/exhaustive/exhaustive_test.h
@@ -0,0 +1,26 @@
+//===-- Exhaustive test template for math functions -------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+// To test exhaustively for inputs in the range [start, stop) in parallel:
+// 1. Inherit from LlvmLibcExhaustiveTest class
+// 2. Overide the test method: void check(T, T, RoundingMode)
+// 4. Call: test_full_range(start, stop, nthreads, rounding)
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+template <typename T>
+struct LlvmLibcExhaustiveTest : public __llvm_libc::testing::Test {
+ // Break [start, stop) into `nthreads` subintervals and apply *check to each
+ // subinterval in parallel.
+ void test_full_range(T start, T stop, int nthreads,
+ mpfr::RoundingMode rounding);
+
+ virtual void check(T start, T stop, mpfr::RoundingMode rounding);
+};
diff --git a/libc/test/src/math/exhaustive/logf_test.cpp b/libc/test/src/math/exhaustive/logf_test.cpp
index abed0731d8992..85827a48d0bff 100644
--- a/libc/test/src/math/exhaustive/logf_test.cpp
+++ b/libc/test/src/math/exhaustive/logf_test.cpp
@@ -6,21 +6,46 @@
//
//===----------------------------------------------------------------------===//
+#include "exhaustive_test.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/math/logf.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/FPMatcher.h"
-#include "utils/UnitTest/Test.h"
using FPBits = __llvm_libc::fputil::FPBits<float>;
namespace mpfr = __llvm_libc::testing::mpfr;
-TEST(LlvmLibcLogfExhaustiveTest, AllValues) {
- uint32_t bits = 0U;
- do {
- FPBits xbits(bits);
- float x = float(xbits);
- EXPECT_MPFR_MATCH(mpfr::Operation::Log, x, __llvm_libc::logf(x), 0.5);
- } while (bits++ < 0x7f7f'ffffU);
+struct LlvmLibcLogfExhaustiveTest : public LlvmLibcExhaustiveTest<uint32_t> {
+ void check(uint32_t start, uint32_t stop,
+ mpfr::RoundingMode rounding) override {
+ mpfr::ForceRoundingMode r(rounding);
+ uint32_t bits = start;
+ do {
+ FPBits xbits(bits);
+ float x = float(xbits);
+ EXPECT_MPFR_MATCH(mpfr::Operation::Log, x, __llvm_libc::logf(x), 0.5,
+ rounding);
+ } while (bits++ < stop);
+ }
+};
+
+TEST_F(LlvmLibcLogfExhaustiveTest, RoundNearestTieToEven) {
+ test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
+ mpfr::RoundingMode::Nearest);
+}
+
+TEST_F(LlvmLibcLogfExhaustiveTest, RoundUp) {
+ test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
+ mpfr::RoundingMode::Upward);
+}
+
+TEST_F(LlvmLibcLogfExhaustiveTest, RoundDown) {
+ test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
+ mpfr::RoundingMode::Downward);
+}
+
+TEST_F(LlvmLibcLogfExhaustiveTest, RoundTowardZero) {
+ test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
+ mpfr::RoundingMode::TowardZero);
}
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 7bb3a7fc5f598..5339df7a43ac3 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -16,6 +16,7 @@
#include <cmath>
#include <fenv.h>
#include <memory>
+#include <sstream>
#include <stdint.h>
#include <string>
@@ -571,6 +572,10 @@ ternary_operation_one_output(Operation op, InputType x, InputType y,
}
}
+// Remark: For all the explain_*_error functions, we will use std::stringstream
+// to build the complete error messages before sending it to the outstream `OS`
+// once at the end. This will stop the error messages from interleaving when
+// the tests are running concurrently.
template <typename T>
void explain_unary_operation_single_output_error(Operation op, T input,
T matchValue,
@@ -582,18 +587,20 @@ void explain_unary_operation_single_output_error(Operation op, T input,
MPFRNumber mpfr_result;
mpfr_result = unary_operation(op, input, precision, rounding);
MPFRNumber mpfrMatchValue(matchValue);
- OS << "Match value not within tolerance value of MPFR result:\n"
+ std::stringstream ss;
+ ss << "Match value not within tolerance value of MPFR result:\n"
<< " Input decimal: " << mpfrInput.str() << '\n';
- __llvm_libc::fputil::testing::describeValue(" Input bits: ", input, OS);
- OS << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n';
+ __llvm_libc::fputil::testing::describeValue(" Input bits: ", input, ss);
+ ss << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n';
__llvm_libc::fputil::testing::describeValue(" Match bits: ", matchValue,
- OS);
- OS << '\n' << " MPFR result: " << mpfr_result.str() << '\n';
+ ss);
+ ss << '\n' << " MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
- " MPFR rounded: ", mpfr_result.as<T>(), OS);
- OS << '\n';
- OS << " ULP error: " << std::to_string(mpfr_result.ulp(matchValue))
+ " MPFR rounded: ", mpfr_result.as<T>(), ss);
+ ss << '\n';
+ ss << " ULP error: " << std::to_string(mpfr_result.ulp(matchValue))
<< '\n';
+ OS << ss.str();
}
template void
@@ -616,31 +623,33 @@ void explain_unary_operation_two_outputs_error(
int mpfrIntResult;
MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult,
precision, rounding);
+ std::stringstream ss;
if (mpfrIntResult != libc_result.i) {
- OS << "MPFR integral result: " << mpfrIntResult << '\n'
+ ss << "MPFR integral result: " << mpfrIntResult << '\n'
<< "Libc integral result: " << libc_result.i << '\n';
} else {
- OS << "Integral result from libc matches integral result from MPFR.\n";
+ ss << "Integral result from libc matches integral result from MPFR.\n";
}
MPFRNumber mpfrMatchValue(libc_result.f);
- OS << "Libc floating point result is not within tolerance value of the MPFR "
+ ss << "Libc floating point result is not within tolerance value of the MPFR "
<< "result.\n\n";
- OS << " Input decimal: " << mpfrInput.str() << "\n\n";
+ ss << " Input decimal: " << mpfrInput.str() << "\n\n";
- OS << "Libc floating point value: " << mpfrMatchValue.str() << '\n';
+ ss << "Libc floating point value: " << mpfrMatchValue.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
- " Libc floating point bits: ", libc_result.f, OS);
- OS << "\n\n";
+ " Libc floating point bits: ", libc_result.f, ss);
+ ss << "\n\n";
- OS << " MPFR result: " << mpfr_result.str() << '\n';
+ ss << " MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
- " MPFR rounded: ", mpfr_result.as<T>(), OS);
- OS << '\n'
+ " MPFR rounded: ", mpfr_result.as<T>(), ss);
+ ss << '\n'
<< " ULP error: "
<< std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
+ OS << ss.str();
}
template void explain_unary_operation_two_outputs_error<float>(
@@ -665,17 +674,19 @@ void explain_binary_operation_two_outputs_error(
MPFRNumber mpfr_result = binary_operation_two_outputs(
op, input.x, input.y, mpfrIntResult, precision, rounding);
MPFRNumber mpfrMatchValue(libc_result.f);
+ std::stringstream ss;
- OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'
+ ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'
<< "MPFR integral result: " << mpfrIntResult << '\n'
<< "Libc integral result: " << libc_result.i << '\n'
<< "Libc floating point result: " << mpfrMatchValue.str() << '\n'
<< " MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
- "Libc floating point result bits: ", libc_result.f, OS);
+ "Libc floating point result bits: ", libc_result.f, ss);
__llvm_libc::fputil::testing::describeValue(
- " MPFR rounded bits: ", mpfr_result.as<T>(), OS);
- OS << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
+ " MPFR rounded bits: ", mpfr_result.as<T>(), ss);
+ ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
+ OS << ss.str();
}
template void explain_binary_operation_two_outputs_error<float>(
@@ -701,20 +712,22 @@ void explain_binary_operation_one_output_error(
MPFRNumber mpfr_result =
binary_operation_one_output(op, input.x, input.y, precision, rounding);
MPFRNumber mpfrMatchValue(libc_result);
+ std::stringstream ss;
- OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n';
+ ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n';
__llvm_libc::fputil::testing::describeValue("First input bits: ", input.x,
- OS);
+ ss);
__llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y,
- OS);
+ ss);
- OS << "Libc result: " << mpfrMatchValue.str() << '\n'
+ ss << "Libc result: " << mpfrMatchValue.str() << '\n'
<< "MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
- "Libc floating point result bits: ", libc_result, OS);
+ "Libc floating point result bits: ", libc_result, ss);
__llvm_libc::fputil::testing::describeValue(
- " MPFR rounded bits: ", mpfr_result.as<T>(), OS);
- OS << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
+ " MPFR rounded bits: ", mpfr_result.as<T>(), ss);
+ ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
+ OS << ss.str();
}
template void explain_binary_operation_one_output_error<float>(
@@ -741,23 +754,25 @@ void explain_ternary_operation_one_output_error(
MPFRNumber mpfr_result = ternary_operation_one_output(
op, input.x, input.y, input.z, precision, rounding);
MPFRNumber mpfrMatchValue(libc_result);
+ std::stringstream ss;
- OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str()
+ ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str()
<< " z: " << mpfrZ.str() << '\n';
__llvm_libc::fputil::testing::describeValue("First input bits: ", input.x,
- OS);
+ ss);
__llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y,
- OS);
+ ss);
__llvm_libc::fputil::testing::describeValue("Third input bits: ", input.z,
- OS);
+ ss);
- OS << "Libc result: " << mpfrMatchValue.str() << '\n'
+ ss << "Libc result: " << mpfrMatchValue.str() << '\n'
<< "MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
- "Libc floating point result bits: ", libc_result, OS);
+ "Libc floating point result bits: ", libc_result, ss);
__llvm_libc::fputil::testing::describeValue(
- " MPFR rounded bits: ", mpfr_result.as<T>(), OS);
- OS << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
+ " MPFR rounded bits: ", mpfr_result.as<T>(), ss);
+ ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
+ OS << ss.str();
}
template void explain_ternary_operation_one_output_error<float>(
diff --git a/libc/utils/UnitTest/FPMatcher.cpp b/libc/utils/UnitTest/FPMatcher.cpp
index c86473e61a2a8..a5ef268eaba3c 100644
--- a/libc/utils/UnitTest/FPMatcher.cpp
+++ b/libc/utils/UnitTest/FPMatcher.cpp
@@ -10,6 +10,7 @@
#include "src/__support/FPUtil/FPBits.h"
+#include <sstream>
#include <string>
namespace __llvm_libc {
@@ -30,10 +31,9 @@ uintToHex(T X, size_t Length = sizeof(T) * 2) {
return s;
}
-template <typename ValType>
+template <typename ValType, typename StreamType>
cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, void>
-describeValue(const char *label, ValType value,
- testutils::StreamWrapper &stream) {
+describeValue(const char *label, ValType value, StreamType &stream) {
stream << label;
FPBits<ValType> bits(value);
@@ -49,15 +49,19 @@ describeValue(const char *label, ValType value,
(fputil::ExponentWidth<ValType>::VALUE - 1) / 4 + 1;
constexpr int mantissaWidthInHex =
(fputil::MantissaWidth<ValType>::VALUE - 1) / 4 + 1;
+ constexpr int bitsWidthInHex =
+ sizeof(typename fputil::FPBits<ValType>::UIntType) * 2;
- stream << "Sign: " << (bits.get_sign() ? '1' : '0') << ", "
- << "Exponent: 0x"
+ stream << "0x"
+ << uintToHex<typename fputil::FPBits<ValType>::UIntType>(
+ bits.uintval(), bitsWidthInHex)
+ << ", (S | E | M) = (" << (bits.get_sign() ? '1' : '0') << " | 0x"
<< uintToHex<uint16_t>(bits.get_unbiased_exponent(),
exponentWidthInHex)
- << ", "
- << "Mantissa: 0x"
+ << " | 0x"
<< uintToHex<typename fputil::FPBits<ValType>::UIntType>(
- bits.get_mantissa(), mantissaWidthInHex);
+ bits.get_mantissa(), mantissaWidthInHex)
+ << ")";
}
stream << '\n';
@@ -70,6 +74,11 @@ template void describeValue<double>(const char *, double,
template void describeValue<long double>(const char *, long double,
testutils::StreamWrapper &);
+template void describeValue<float>(const char *, float, std::stringstream &);
+template void describeValue<double>(const char *, double, std::stringstream &);
+template void describeValue<long double>(const char *, long double,
+ std::stringstream &);
+
} // namespace testing
} // namespace fputil
} // namespace __llvm_libc
diff --git a/libc/utils/UnitTest/FPMatcher.h b/libc/utils/UnitTest/FPMatcher.h
index 7f1c75d7e108c..a2c204c24f10d 100644
--- a/libc/utils/UnitTest/FPMatcher.h
+++ b/libc/utils/UnitTest/FPMatcher.h
@@ -17,10 +17,9 @@ namespace __llvm_libc {
namespace fputil {
namespace testing {
-template <typename ValType>
+template <typename ValType, typename StreamType>
cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, void>
-describeValue(const char *label, ValType value,
- testutils::StreamWrapper &stream);
+describeValue(const char *label, ValType value, StreamType &stream);
template <typename T, __llvm_libc::testing::TestCondition Condition>
class FPMatcher : public __llvm_libc::testing::Matcher<T> {
More information about the libc-commits
mailing list