Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to print hashes and computed errors in tests #778

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions clients/common/lapack/testing_sygvx_hegvx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ void sygvx_hegvx_initData(const rocblas_handle handle,
// for testing purposes, we start with a reduced matrix M for the standard equivalent problem
// with spectrum in a desired range (-20, 20). Then we construct the generalized pair
// (A, B) from there.

[[maybe_unused]] volatile auto mptr = memset(hB[b], 0, n * ldb * sizeof(T));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the volatile auto mptr is an attempt to ensure that memset is not optimized out, but I'm not sure that's guaranteed. Why is this needed?

for(rocblas_int i = 0; i < n; i++)
{
// scale matrices and set hA = M (symmetric/hermitian), hB = U (upper triangular)
Expand Down Expand Up @@ -424,6 +426,16 @@ void sygvx_hegvx_getError(const rocblas_handle handle,
sygvx_hegvx_initData<true, true, T>(handle, itype, evect, n, dA, lda, stA, dB, ldb, stB, bc, hA,
hB, A, B, true, singular);

//
// Compute input data hash (combine matrices A and B)
//
std::size_t input_hash = 0;
for(rocblas_int b = 0; b < bc; ++b)
{
input_hash = hash_combine(input_hash, hA[0], lda * n);
input_hash = hash_combine(input_hash, hB[0], ldb * n);
}

// execute computations
// GPU lapack
CHECK_ROCBLAS_ERROR(rocsolver_sygvx_hegvx(STRIDED, handle, itype, evect, erange, uplo, n,
Expand Down Expand Up @@ -472,6 +484,41 @@ void sygvx_hegvx_getError(const rocblas_handle handle,
*max_err += 1;
}

//
// Compute output hashes
//
std::size_t rocsolver_eigenvalues_hash = 0;
std::size_t rocsolver_eigenvectors_hash = 0;

for(rocblas_int b = 0; b < bc; ++b)
{
if(hInfo[b][0] == 0)
{
rocsolver_eigenvalues_hash
= hash_combine(rocsolver_eigenvalues_hash, hWRes[b], hNevRes[b][0]);

if(evect == rocblas_evect_original)
{
rocsolver_eigenvectors_hash
= hash_combine(rocsolver_eigenvectors_hash, hZRes[b], hNevRes[b][0] * ldz);
}
}
}

//
// Print hashes
//
ROCSOLVER_GTEST_MSG_PRINTER << "Input matrix hash: " << input_hash << std::endl << std::flush;
ROCSOLVER_GTEST_MSG_PRINTER << "Rocsolver eigenvalues hash: " << rocsolver_eigenvalues_hash
<< std::endl
<< std::flush;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::endl is defined as a newline and a flush, so this is redundant. Although, again, I would suggest using fmt rather than iostreams.

if(evect == rocblas_evect_original)
{
ROCSOLVER_GTEST_MSG_PRINTER
<< "Rocsolver eigenvectors hash: " << rocsolver_eigenvectors_hash << std::endl
<< std::flush;
}

double err;

for(rocblas_int b = 0; b < bc; ++b)
Expand Down
97 changes: 96 additions & 1 deletion clients/common/misc/rocsolver_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,49 @@ namespace fs = std::experimental::filesystem;
#define USE_ROCBLAS_REALLOC_ON_DEMAND true

#ifdef ROCSOLVER_CLIENTS_TEST
#define ROCSOLVER_TEST_CHECK(T, max_error, tol) ASSERT_LE((max_error), (tol)*get_epsilon<T>())

#ifdef ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES
// Format output similarly as GTEST messages
#define ANSI_CODE_GTEST_GREEN "\033[0;32m"
#define ANSI_CODE_NORMAL_TERM "\033[0;0m"
Comment on lines +57 to +58
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to print with colour, we should follow Google Test's lead by obeying the gtest_color option and using TTY detection for the default. I'd suggest printing without colour for now, as that is a bit of a distraction from the main purpose of this change.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the fmtlib that we use in rocsolver has a color API. https://fmt.dev/11.0/api/#color-api

// Macro ROCSOLVER_GTEST_MSG_PRINTER is also used to print hashes in tests
#define ROCSOLVER_GTEST_MSG_PRINTER \
std::cout << ANSI_CODE_GTEST_GREEN << "[ ] " << ANSI_CODE_NORMAL_TERM
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use fmt. We added it as a dependency to do atomic prints and avoid the iostreams interfaces where the printing of a line may be split into multiple function calls. It's not that important at the moment, but it's an invariant we inherited from rocfft / rocblas and I want to retain it in case we do ever run tests in parallel.

// Print computed errors for all tests, making sure that there are sufficient digits to uniquely
// represent all distinct `double` values.
#define ROCSOLVER_STRNGFY(s) #s
#define ROCSOLVER_PRINT_TEST_ERROR(T, max_error, tol) ROCSOLVER_PRINT_TEST_ERROR2(T, max_error, tol)
#define ROCSOLVER_PRINT_TEST_ERROR2(T, max_error, tol) \
do \
{ \
const auto default_precision{std::cout.precision()}; \
constexpr auto max_precision{std::numeric_limits<double>::max_digits10 + 1}; \
double tol_ = static_cast<double>(tol) * static_cast<double>(get_epsilon<T>()); \
double max_error_ = static_cast<double>(max_error); \
ROCSOLVER_GTEST_MSG_PRINTER \
<< "Computed error: " << ROCSOLVER_STRNGFY(max_error) << " / " \
<< ROCSOLVER_STRNGFY(((tol)*get_epsilon<T>())) << " = " \
<< std::setprecision(max_precision) \
<< ((max_error_ >= 0.) && (tol_ > 0.) ? max_error_ / tol_ : -1.) \
<< std::setprecision(default_precision) << std::endl \
<< std::flush; \
Comment on lines +73 to +79
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be so much nicer using the fmtlib interface.

} while(0)
#else // #ifdef ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES
static std::stringstream rocsolver_discard_tests_extra_messages;
#define ROCSOLVER_GTEST_MSG_PRINTER rocsolver_discard_tests_extra_messages
#define ROCSOLVER_PRINT_TEST_ERROR(T, max_error, tol)
#endif // #ifdef ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES

#define ROCSOLVER_TEST_CHECK(T, max_error, tol) \
do \
{ \
ASSERT_LE((max_error), (tol)*get_epsilon<T>()); \
ROCSOLVER_PRINT_TEST_ERROR(T, max_error, tol); \
} while(0)

#else // ROCSOLVER_CLIENTS_BENCH
static std::stringstream rocsolver_discard_tests_extra_messages;
#define ROCSOLVER_GTEST_MSG_PRINTER rocsolver_discard_tests_extra_messages
#define ROCSOLVER_TEST_CHECK(T, max_error, tol)
#endif

Expand Down Expand Up @@ -158,3 +199,57 @@ inline std::ostream& operator<<(std::ostream& os, printable_char x)

// location of the sparse data directory for the re-factorization tests
fs::path get_sparse_data_dir();

/// Combines `seed` with the hash of `value`, following the spirit of
/// `boost::hash_combine`.
///
/// Extends `std::hash` to combine the hashes of multiple values (e.g.,
/// from an array).
///
/// Attention: hash_combine(0, T(0)) != 0
template <typename T>
std::size_t hash_combine(std::size_t seed, T value)
{
using S = decltype(std::real(T{}));
auto hasher = std::hash<S>();

if constexpr(rocblas_is_complex<T>)
{
seed ^= hasher(std::real(value)) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= hasher(std::imag(value)) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
else
{
seed ^= hasher(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

return seed;
}

/// Hash contents of the given array.
///
/// If seed == 0 and array_size == 0, then hash_combine(seed, _, array_size) == 0
template <typename T>
std::size_t hash_combine(std::size_t seed, T const* array, std::size_t array_size)
{
std::size_t hash = 0;
if(array_size > 0)
{
hash = hash_combine(seed, array_size);
for(std::size_t i = 0; i < array_size; ++i)
{
hash = hash_combine(hash, array[i]);
}
}

return hash;
}

/// Hash contents of the given array.
///
/// If seed == 0 and array.size() == 0, then hash_combine(seed, array) == 0
template <typename T>
std::size_t hash_combine(std::size_t seed, const std::vector<T>& array)
{
return hash_combine(seed, array.data(), array.size());
}
1 change: 1 addition & 0 deletions clients/gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ target_compile_options(rocsolver-test PRIVATE -mf16c)
target_compile_definitions(rocsolver-test PRIVATE
ROCM_USE_FLOAT16
ROCSOLVER_CLIENTS_TEST
ROCSOLVER_CLIENTS_TEST_PRINT_EXTRA_MESSAGES
)

add_test(
Expand Down