From 9d9eb9603ab05c11a7b1e371b4995bf7a76f6b47 Mon Sep 17 00:00:00 2001 From: Julio Machado Silva Date: Fri, 2 Aug 2024 14:03:02 -0600 Subject: [PATCH 1/2] Add functionality to print hashes and computed errors in tests rocSOLVER's tests currently output no useful information for passing tests, which makes it hard to ascertain when new code adversely affects the accuracy of methods while also hindering the investigation of bugs that may affect determinism. This commit adds code to print the computed error in each test and hashing functions to allow printing hashes of input and output matrices, when desired. --- clients/common/lapack/testing_sygvx_hegvx.hpp | 44 +++++++++ clients/common/misc/rocsolver_test.hpp | 97 ++++++++++++++++++- clients/gtest/CMakeLists.txt | 1 + 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/clients/common/lapack/testing_sygvx_hegvx.hpp b/clients/common/lapack/testing_sygvx_hegvx.hpp index 031fe1f12..77ef9c052 100644 --- a/clients/common/lapack/testing_sygvx_hegvx.hpp +++ b/clients/common/lapack/testing_sygvx_hegvx.hpp @@ -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)); for(rocblas_int i = 0; i < n; i++) { // scale matrices and set hA = M (symmetric/hermitian), hB = U (upper triangular) @@ -424,6 +426,13 @@ void sygvx_hegvx_getError(const rocblas_handle handle, sygvx_hegvx_initData(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; + input_hash = hash_combine(input_hash, hA[0], lda * n * bc); + input_hash = hash_combine(input_hash, hB[0], ldb * n * bc); + // execute computations // GPU lapack CHECK_ROCBLAS_ERROR(rocsolver_sygvx_hegvx(STRIDED, handle, itype, evect, erange, uplo, n, @@ -472,6 +481,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; + 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) diff --git a/clients/common/misc/rocsolver_test.hpp b/clients/common/misc/rocsolver_test.hpp index e97ff794d..4e8cac82d 100644 --- a/clients/common/misc/rocsolver_test.hpp +++ b/clients/common/misc/rocsolver_test.hpp @@ -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()) + +#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" +// 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 +// 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::max_digits10 + 1}; \ + double tol_ = static_cast(tol) * static_cast(get_epsilon()); \ + double max_error_ = static_cast(max_error); \ + ROCSOLVER_GTEST_MSG_PRINTER \ + << "Computed error: " << ROCSOLVER_STRNGFY(max_error) << " / " \ + << ROCSOLVER_STRNGFY(((tol)*get_epsilon())) << " = " \ + << std::setprecision(max_precision) \ + << ((max_error_ >= 0.) && (tol_ > 0.) ? max_error_ / tol_ : -1.) \ + << std::setprecision(default_precision) << std::endl \ + << std::flush; \ + } 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()); \ + 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 @@ -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 +std::size_t hash_combine(std::size_t seed, T value) +{ + using S = decltype(std::real(T{})); + auto hasher = std::hash(); + + if constexpr(rocblas_is_complex) + { + 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 +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 +std::size_t hash_combine(std::size_t seed, const std::vector& array) +{ + return hash_combine(seed, array.data(), array.size()); +} diff --git a/clients/gtest/CMakeLists.txt b/clients/gtest/CMakeLists.txt index 6a5ed42b0..38eeda2c9 100755 --- a/clients/gtest/CMakeLists.txt +++ b/clients/gtest/CMakeLists.txt @@ -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( From 37d1b6266806fbedd1e277ab9c6e1277adcdeb5e Mon Sep 17 00:00:00 2001 From: Julio Machado Silva Date: Fri, 2 Aug 2024 16:47:17 -0600 Subject: [PATCH 2/2] Fix hashing of input matrices on SYGVX tests --- clients/common/lapack/testing_sygvx_hegvx.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clients/common/lapack/testing_sygvx_hegvx.hpp b/clients/common/lapack/testing_sygvx_hegvx.hpp index 77ef9c052..239640755 100644 --- a/clients/common/lapack/testing_sygvx_hegvx.hpp +++ b/clients/common/lapack/testing_sygvx_hegvx.hpp @@ -430,8 +430,11 @@ void sygvx_hegvx_getError(const rocblas_handle handle, // Compute input data hash (combine matrices A and B) // std::size_t input_hash = 0; - input_hash = hash_combine(input_hash, hA[0], lda * n * bc); - input_hash = hash_combine(input_hash, hB[0], ldb * n * bc); + 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