mirror of
https://github.com/amd/blis.git
synced 2026-07-11 09:40:57 +00:00
* Bitexactness CRC verification and per-test JSON output * Remove redundant BLIS_TEST_SEED random seed utilities The random_seed_utils.h and BLIS_TEST_SEED environment variable are unnecessary since the codebase already ensures deterministic random number generation via RANDOM_POOL_SEED and SRAND_SEED constants hardcoded in testing_helpers.h. * Add CRC support for integer/char computediff and cache env var checks Add CRC calculation and binary output to the gtint_t and char specializations of computediff, matching the pattern used by all other overloads. Char values are widened to gtint_t for safe uint32_t-aligned CRC access. Cache BLIS_ENABLE_CRC and BLIS_ENABLE_BINARY_OUTPUT env var lookups via static const bool in is_crc_enabled() and is_binary_output_enabled(). Guard all CRC/binary blocks in computediff with is_any_verification_enabled() so the common disabled path is a single static bool read with zero allocations. * Address PR review comments and refactor computediff CRC blocks Refactor: Extract duplicated CRC/binary-output blocks from all 8 computediff overloads into verify_vector_data and verify_matrix_data helpers in blis_test_utils namespace. Bug fixes from PR review: add missing includes (cstdlib, utility), enforce MAX_OUTPUT_SIZE_BYTES limit with integer overflow guard, add buffer validation in all CRC generation functions, add default case to FLA_GET_DATATYPE_FACTOR macro, replace deprecated test_case_name() with test_suite_name(), add MAKE_DIRECTORY error checking in CMake, and update copyright years to 2026. * Refactored crc_utils based on review comments. * binary_output_utils.h cleanup. * Address PR review comments: remove unused functions and fix copyright years. Remove unused generate_crc_matrix, generate_crc_matrix_no_nb_diag, generate_crc_matrix_no_nb_diag_with_storage, and calculate_and_print_matrix_crc from crc_utils.h. Remove unused calculate_and_print_matrix_hash from check_error.h. Fix copyright year to 2026 only in crc_utils.h and binary_output_utils.h. Remove (Performance) label from CRC heading in README.md. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix for review comments. * Address review comments: rename verify to collect, consistent void returns, remove filename prefix - Rename verify_vector_data/verify_matrix_data to collect_vector_data/ collect_matrix_data since these functions only collect CRC and binary output data without performing comparison. - Make return types consistent: change calculate_and_print_crc, calculate_and_print_matrix_crc_with_storage, format_and_record_crc, and write_comparison_outputs to return void since return values were never used. - Remove redundant test_output_ prefix from generate_binary_filename to avoid duplication with the blis_test_outputs/ directory. - Remove unused utility include from binary_output_utils.h. - Update README wording from compiled out to disabled. Made-with: Cursor * Fix strict aliasing, use if constexpr, zero-pad CRC hex, separate feature guards - Replace reinterpret_cast<uint32_t*> with memcpy-based read_uint32() helper to avoid strict-aliasing UB on float/double/complex buffers. Produces identical CRC values. - Use if constexpr(CRC_ENABLED) instead of runtime if(!CRC_ENABLED) to prevent CRC template instantiation when ENABLE_CRC is off. - Zero-pad CRC hex output to 8 digits for stable downstream comparison. - Separate ENABLE_CRC and ENABLE_BINARY_OUTPUT preprocessor guards in verification_utils.h so each feature is compiled independently. Made-with: Cursor * Handle write_binary_output return values in write_comparison_outputs Capture the bool return values from write_binary_output and, on failure, log a warning to stdout and record the error as a GTest property. This keeps binary output as a non-fatal diagnostic aid while ensuring return values are explicitly used. Made-with: Cursor --------- Co-authored-by: Anuraj <avettick@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
582 lines
29 KiB
C++
582 lines
29 KiB
C++
/*
|
|
|
|
BLIS
|
|
An object-based framework for developing high-performance BLAS-like
|
|
libraries.
|
|
|
|
Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
- Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
- Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
- Neither the name(s) of the copyright holder(s) nor the names of its
|
|
contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
#include "blis.h"
|
|
#include <gtest/gtest.h>
|
|
#include "common/testing_helpers.h"
|
|
#include "common/verification_utils.h"
|
|
|
|
/**
|
|
* This file includes the functionality used to determine correctness of the results.
|
|
* We compare the results component-wise, meaning that for two scalars, we compare the scalars,
|
|
* and for two vectors or matrices we compare each element of the vector or matrix respectively.
|
|
*
|
|
* We have two separate cases:
|
|
* 1) it's meaningful to have NaNs and/or Infs at the results,
|
|
* because we are testing the correct propagation of those values.
|
|
* In this case the results could be either extreme values, or f.p. values,
|
|
* and we need to be able to compare all of those following the rules below:
|
|
* - if there are NaNs/Infs, check if both reference and blis solution has
|
|
* NaN/Inf accordingly. Remember that for Infs we need to check the sign as well.
|
|
* - if there are no NaNs/Infs, either do the bitwise comparison (if that's desired),
|
|
* or call into getError() function. getError() will check if reference is less
|
|
* than one, in which case will compute the absolute error, otherwise compute the
|
|
* relative error.
|
|
* - for complex numbers, we need to check for all possible combinations of NaN/Inf/FP
|
|
* for real and imaginary parts. So that will be a combination of the two steps above.
|
|
* 2) it's not meaningful to check for NaNs and Infs and we expect only FP values in the
|
|
* results. In this case, we either do a bitwise comparison or call into getError() directly.
|
|
*
|
|
* Note that all operations with a NaN/Inf will lead to either comparison with a NaN, or
|
|
* inf < thresh, which always return false; so NumericalComparisonFPOnly() will return failure.
|
|
* So, for the case where we do not expect NaN/Infs, we want to fail if NaN and Infs are
|
|
* present so that we do not have tests passing when it doesn't make sense to do so.
|
|
* For an example of such case, think of a triangular solver with zeros on the diagonal.
|
|
*
|
|
*/
|
|
|
|
// Enum used to do the correct printing depending on what we aim to compare.
|
|
enum ObjType {
|
|
SCALAR,
|
|
VECTOR,
|
|
MATRIX
|
|
};
|
|
// Enum used to do the correct comparison for NaNs, depending on whether we
|
|
// compare the real or the imaginary component.
|
|
enum ComplexPart {
|
|
REAL,
|
|
IMAGINARY
|
|
};
|
|
|
|
// Helper class to be used to pass info into the Comparators.
|
|
struct ComparisonHelper{
|
|
double threshold;
|
|
ObjType object_type;
|
|
gtint_t i; // used to print vector/matrix elements that we compare
|
|
gtint_t j; // used to print matrix elements that we compare
|
|
bool binary_comparison; // By default compare using relative error or absolute error approach.
|
|
bool nan_inf_check; //By default do not check for NaNs and Infs.
|
|
|
|
// Constructor for the case of binary_comparison, no threshold.
|
|
ComparisonHelper(ObjType object_type) : threshold(-13.0),
|
|
object_type(object_type),
|
|
i(-11),
|
|
j(-11),
|
|
binary_comparison(false),
|
|
nan_inf_check(false) {};
|
|
// Constructor for the generic case where threshold is used.
|
|
ComparisonHelper(ObjType object_type, double threshold) : threshold(threshold),
|
|
object_type(object_type),
|
|
i(-11),
|
|
j(-11),
|
|
binary_comparison(false),
|
|
nan_inf_check(false) {};
|
|
};
|
|
|
|
// Generic comparison of f.p. numbers that doesn't check for NaN's and Infs:
|
|
template<typename T, typename ErrorMessageFunc>
|
|
testing::AssertionResult NumericalComparisonFPOnly(const char* blis_sol_char,
|
|
const char* ref_sol_char,
|
|
const char* comp_helper_char,
|
|
const T blis_sol,
|
|
const T ref_sol,
|
|
const ComparisonHelper comp_helper,
|
|
ErrorMessageFunc error_message_func)
|
|
{
|
|
if (comp_helper.binary_comparison)
|
|
{
|
|
if (blis_sol == ref_sol) return testing::AssertionSuccess();
|
|
return testing::AssertionFailure() << error_message_func();
|
|
}
|
|
else {
|
|
double error = testinghelpers::getError(blis_sol,ref_sol);
|
|
if (error <= comp_helper.threshold) return testing::AssertionSuccess();
|
|
using RT = typename testinghelpers::type_info<T>::real_type;
|
|
return testing::AssertionFailure() << error_message_func()
|
|
<< ", thresh = " << comp_helper.threshold
|
|
<< ", error = " << error
|
|
<< " (" << error/std::numeric_limits<RT>::epsilon() << " * eps)";
|
|
}
|
|
}
|
|
|
|
// NaN/Inf comparison for real numbers
|
|
template<typename T, typename ErrorMessageFunc>
|
|
testing::AssertionResult NumericalComparisonRealNaNInf(const char* blis_sol_char,
|
|
const char* ref_sol_char,
|
|
const char* comp_helper_char,
|
|
const T blis_sol,
|
|
const T ref_sol,
|
|
const ComparisonHelper comp_helper,
|
|
ErrorMessageFunc error_message_func)
|
|
{
|
|
// if both are NaN return SUCCESS
|
|
if ((std::isnan(ref_sol)) && (std::isnan(blis_sol)))
|
|
return testing::AssertionSuccess();
|
|
// if only one of them is NaN, return FAILURE
|
|
else if ((std::isnan(ref_sol)) || (std::isnan(blis_sol)))
|
|
return testing::AssertionFailure() << error_message_func();
|
|
// if both are inf check the sign
|
|
else if ((std::isinf(ref_sol)) && (std::isinf(blis_sol)))
|
|
{
|
|
// check the sign of infs
|
|
if( ref_sol == blis_sol ) return testing::AssertionSuccess();
|
|
// both are infs but have different signs, return FAILURE.
|
|
else return testing::AssertionFailure() << error_message_func();
|
|
}
|
|
// if only one of them is Inf
|
|
else if ((std::isinf(ref_sol)) || (std::isinf(blis_sol)))
|
|
return testing::AssertionFailure() << error_message_func();
|
|
// If neither reference nor BLIS sol is NaN/Inf do simple comparison, based on relative or absolute error.
|
|
else return NumericalComparisonFPOnly<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, error_message_func);
|
|
}
|
|
|
|
// Comparison for complex numbers in the case of NaNs.
|
|
// Will be re-used for comparison of real and imaginary components.
|
|
template<typename T, typename RT = typename testinghelpers::type_info<T>::real_type, typename ErrorMessageFunc>
|
|
testing::AssertionResult NumericalComparisonNaN(const char* blis_sol_char,
|
|
const char* ref_sol_char,
|
|
const char* comp_helper_char,
|
|
const T blis_sol,
|
|
const T ref_sol,
|
|
const ComparisonHelper comp_helper,
|
|
const ComplexPart complex_part,
|
|
ErrorMessageFunc error_message_func)
|
|
{
|
|
// Assign values to intermediate variables as if we are comparing the real part.
|
|
RT ref_sol_1 = ref_sol.real, ref_sol_2 = ref_sol.imag, blis_sol_1 = blis_sol.real, blis_sol_2 = blis_sol.imag;
|
|
// if we are comparing based on the imaginary part update the values.
|
|
if (complex_part == IMAGINARY)
|
|
{
|
|
ref_sol_2 = ref_sol.real;
|
|
ref_sol_1 = ref_sol.imag;
|
|
blis_sol_2 = blis_sol.real;
|
|
blis_sol_1 = blis_sol.imag;
|
|
}
|
|
// Check if the both parts are NaNs.
|
|
if ((std::isnan(ref_sol_1)) && (std::isnan(blis_sol_1)))
|
|
// Check second part for equality based on real NaN/Inf comparison.
|
|
return NumericalComparisonRealNaNInf<RT>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol_2, ref_sol_2, comp_helper, error_message_func);
|
|
// if only one of the first parts is NaN
|
|
return testing::AssertionFailure() << error_message_func();
|
|
}
|
|
|
|
// Comparison for complex numbers in the case of Infs.
|
|
// Will be re-used for comparison of real and imaginary components.
|
|
template<typename T, typename RT = typename testinghelpers::type_info<T>::real_type, typename ErrorMessageFunc>
|
|
testing::AssertionResult NumericalComparisonInf(const char* blis_sol_char,
|
|
const char* ref_sol_char,
|
|
const char* comp_helper_char,
|
|
const T blis_sol,
|
|
const T ref_sol,
|
|
const ComparisonHelper comp_helper,
|
|
const ComplexPart complex_part,
|
|
ErrorMessageFunc error_message_func)
|
|
{
|
|
// Assign values to intermediate variables as if we are comparing the real part.
|
|
RT ref_sol_1 = ref_sol.real, ref_sol_2 = ref_sol.imag, blis_sol_1 = blis_sol.real, blis_sol_2 = blis_sol.imag;
|
|
// if we are comparing based on the imaginary part update the values.
|
|
if (complex_part == IMAGINARY)
|
|
{
|
|
ref_sol_2 = ref_sol.real;
|
|
ref_sol_1 = ref_sol.imag;
|
|
blis_sol_2 = blis_sol.real;
|
|
blis_sol_1 = blis_sol.imag;
|
|
}
|
|
// check if both of the first parts are inf
|
|
if ((std::isinf(ref_sol_1)) && (std::isinf(blis_sol_1)))
|
|
{
|
|
// check the sign of infs
|
|
if( ref_sol_1 == blis_sol_1 )
|
|
// Check second part for equality based on real NaN/Inf comparison.
|
|
return NumericalComparisonRealNaNInf<RT>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol_2, ref_sol_2, comp_helper, error_message_func);
|
|
// if both are infs but have different signs, return FAILURE.
|
|
else return testing::AssertionFailure() << error_message_func();
|
|
}
|
|
// if only one of them is Inf
|
|
return testing::AssertionFailure() << error_message_func();
|
|
}
|
|
|
|
// Comparisons that take into account the presence of NaNs and Infs, printing variable name:
|
|
template<typename T, typename RT = typename testinghelpers::type_info<T>::real_type>
|
|
testing::AssertionResult NumericalComparison(const char* var_name_char,
|
|
const char* blis_sol_char,
|
|
const char* ref_sol_char,
|
|
const char* comp_helper_char,
|
|
std::string var_name,
|
|
const T blis_sol,
|
|
const T ref_sol,
|
|
const ComparisonHelper comp_helper)
|
|
{
|
|
// Lazy string construction - only create error message when actually needed
|
|
auto create_error_message = [&]() -> std::string {
|
|
// Base error message used for scalar values
|
|
std::string error_message = var_name_char;
|
|
error_message += " = " + var_name + ", ";
|
|
error_message += blis_sol_char;
|
|
error_message += " = " + testinghelpers::to_string(blis_sol) + ", ";
|
|
error_message += ref_sol_char;
|
|
error_message += " = " + testinghelpers::to_string(ref_sol);
|
|
// If we are comparing a vector, update error message to include the current index
|
|
if(comp_helper.object_type == VECTOR)
|
|
error_message += ", i = " + std::to_string(comp_helper.i);
|
|
// If we are comparing a matrix, update error message to include the current indices
|
|
else if(comp_helper.object_type == MATRIX)
|
|
error_message += ", i = " + std::to_string(comp_helper.i) + ", j = " + std::to_string(comp_helper.j);
|
|
return error_message;
|
|
};
|
|
|
|
// Check if NaN/Inf comparison is necessary and if so, proceed.
|
|
// Otherwise, call numerical comparison only, without considering NaNs and Infs.
|
|
if (comp_helper.nan_inf_check)
|
|
{
|
|
if constexpr (testinghelpers::type_info<T>::is_real)
|
|
return NumericalComparisonRealNaNInf<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, create_error_message);
|
|
// If it's complex we need to check real and imaginary parts.
|
|
else
|
|
{
|
|
// Check if any of the real parts is NaN, and if so, call into NaN comparator.
|
|
if ((std::isnan(ref_sol.real)) || (std::isnan(blis_sol.real)))
|
|
{
|
|
ComplexPart complex_part = REAL;
|
|
return NumericalComparisonNaN<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
|
|
}
|
|
// Check if any of the imag parts is NaN, and if so, call into NaN comparator.
|
|
else if ((std::isnan(ref_sol.imag)) || (std::isnan(blis_sol.imag)))
|
|
{
|
|
ComplexPart complex_part = IMAGINARY;
|
|
return NumericalComparisonNaN<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
|
|
}
|
|
// Check if any of the real parts is Inf, and if so, call into Inf comparator.
|
|
else if ((std::isinf(ref_sol.real)) || (std::isinf(blis_sol.real)))
|
|
{
|
|
ComplexPart complex_part = REAL;
|
|
return NumericalComparisonInf<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
|
|
}
|
|
// Check if any of the imag parts is NaN or Inf, and if so, call into Inf comparator.
|
|
else if ((std::isinf(ref_sol.imag)) || (std::isinf(blis_sol.imag)))
|
|
{
|
|
ComplexPart complex_part = IMAGINARY;
|
|
return NumericalComparisonInf<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
|
|
}
|
|
// If neither reference nor BLIS sol is NaN or Inf, or if NaN/Inf checks are not necessary,
|
|
// do simple comparison, based on relative or absolute error.
|
|
else
|
|
return NumericalComparisonFPOnly<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, create_error_message);
|
|
}
|
|
}
|
|
// If NaN/Inf checks are not necessary, do simple comparison, based on relative or absolute error.
|
|
else
|
|
return NumericalComparisonFPOnly<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, create_error_message);
|
|
}
|
|
|
|
/**
|
|
* Binary comparison of two scalars, printing variable name.
|
|
*/
|
|
template <typename T>
|
|
void computediff( std::string var_name, T blis_sol, T ref_sol, bool nan_inf_check = false )
|
|
{
|
|
testinghelpers::verification::collect_vector_data(var_name, &blis_sol, &ref_sol, 1);
|
|
|
|
ComparisonHelper comp_helper(SCALAR);
|
|
comp_helper.binary_comparison = true;
|
|
comp_helper.nan_inf_check = nan_inf_check;
|
|
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol, ref_sol, comp_helper);
|
|
}
|
|
|
|
/**
|
|
* Relative comparison of two scalars, using a threshold, printing variable name.
|
|
*/
|
|
template <typename T>
|
|
void computediff( std::string var_name, T blis_sol, T ref_sol, double thresh, bool nan_inf_check = false )
|
|
{
|
|
testinghelpers::verification::collect_vector_data(var_name, &blis_sol, &ref_sol, 1);
|
|
|
|
ComparisonHelper comp_helper(SCALAR, thresh);
|
|
comp_helper.nan_inf_check = nan_inf_check;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol, ref_sol, comp_helper);
|
|
}
|
|
|
|
/**
|
|
* Binary comparison of two vectors with length n and increment inc, printing variable name.
|
|
*/
|
|
template <typename T>
|
|
void computediff( std::string var_name, gtint_t n, T *blis_sol, T *ref_sol, gtint_t inc, bool nan_inf_check = false )
|
|
{
|
|
testinghelpers::verification::collect_vector_data(var_name, blis_sol, ref_sol, testinghelpers::buff_dim(n, inc));
|
|
|
|
gtint_t abs_inc = std::abs(inc);
|
|
ComparisonHelper comp_helper(VECTOR);
|
|
comp_helper.nan_inf_check = nan_inf_check;
|
|
comp_helper.binary_comparison = true;
|
|
|
|
// If increment is zero, we just have one element to compare.
|
|
if (abs_inc == 0)
|
|
n = 1;
|
|
|
|
// In case inc is negative in a call to BLIS APIs, we just access it from the end to the beginning,
|
|
// so practically nothing changes. Access from beginning to end to optimize memory operations.
|
|
for (gtint_t i = 0; i < n; i++)
|
|
{
|
|
comp_helper.i = i;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc], ref_sol[i*abs_inc], comp_helper) << "inc = " << inc;
|
|
// Go through elements that are part of the array that should not have been modified by the
|
|
// call to a BLIS API. Use the bitwise comparison for this case.
|
|
if (i < n-1)
|
|
{
|
|
for (gtint_t j = 1; j < abs_inc; j++)
|
|
{
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc + j], ref_sol[i*abs_inc + j], comp_helper) << "inc = " << inc << " This element is expected to not be modified.";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Relative comparison of two vectors with length n and increment inc, printing variable name.
|
|
*/
|
|
template <typename T>
|
|
void computediff( std::string var_name, gtint_t n, T *blis_sol, T *ref_sol, gtint_t inc, double thresh, bool nan_inf_check = false )
|
|
{
|
|
testinghelpers::verification::collect_vector_data(var_name, blis_sol, ref_sol, testinghelpers::buff_dim(n, inc));
|
|
|
|
gtint_t abs_inc = std::abs(inc);
|
|
ComparisonHelper comp_helper(VECTOR, thresh);
|
|
comp_helper.nan_inf_check = nan_inf_check;
|
|
|
|
// If increment is zero, we just have one element to compare.
|
|
if (abs_inc == 0)
|
|
n = 1;
|
|
// In case inc is negative in a call to BLIS APIs, we just access it from the end to the beginning,
|
|
// so practically nothing changes. Access from beginning to end to optimize memory operations.
|
|
for (gtint_t i = 0; i < n; i++)
|
|
{
|
|
comp_helper.i = i;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc], ref_sol[i*abs_inc], comp_helper) << "inc = " << inc;
|
|
// Go through elements that are part of the array that should not have been modified by the
|
|
// call to a BLIS API. Use the bitwise comparison for this case.
|
|
if (i < n-1)
|
|
{
|
|
for (gtint_t j = 1; j < abs_inc; j++)
|
|
{
|
|
comp_helper.binary_comparison = true;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc + j], ref_sol[i*abs_inc + j], comp_helper) << "inc = " << inc << " This element is expected to not be modified.";
|
|
}
|
|
comp_helper.binary_comparison = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Binary comparison of two matrices with dimensions m-by-n and leading dimension ld, printing variable name.
|
|
*/
|
|
template <typename T>
|
|
void computediff(std::string var_name, char storage, gtint_t m, gtint_t n, T *blis_sol, T *ref_sol, gtint_t ld, bool nan_inf_check = false )
|
|
{
|
|
testinghelpers::verification::collect_matrix_data(var_name, blis_sol, ref_sol, m, n, ld, storage);
|
|
|
|
gtint_t i,j;
|
|
ComparisonHelper comp_helper(MATRIX);
|
|
comp_helper.nan_inf_check = nan_inf_check;
|
|
comp_helper.binary_comparison = true;
|
|
// Loop for column-major order
|
|
if( (storage == 'c') || (storage == 'C') )
|
|
{
|
|
for( j = 0 ; j < n ; j++ )
|
|
{
|
|
// First iterate through the elements of the arrays that are part of the matrix
|
|
// and are expected to be modified by a call to BLIS APIs.
|
|
for( i = 0 ; i < m ; i++ )
|
|
{
|
|
comp_helper.i = i;
|
|
comp_helper.j = j;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper);
|
|
}
|
|
// Now iterate through the rest of elements in memory space that are not part of the matrix,
|
|
// so we use binary comparison to verify that are exactly the same as the reference.
|
|
// Since to get create the data we use a copy to initialize the reference results, those
|
|
// elements are expected to identical.
|
|
for (i = (std::max)(m,(gtint_t)0); i < ld; i++)
|
|
{
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper) << "This element is expected to not be modified.";
|
|
}
|
|
}
|
|
}
|
|
// Loop for row-major order
|
|
else
|
|
{
|
|
for( i = 0 ; i < m ; i++ )
|
|
{
|
|
// First iterate through the elements of the arrays that are part of the matrix
|
|
// and are expected to be modified by a call to BLIS APIs.
|
|
for( j = 0 ; j < n ; j++ )
|
|
{
|
|
comp_helper.i = i;
|
|
comp_helper.j = j;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper);
|
|
}
|
|
// Now iterate through the rest of elements in memory space that are not part of the matrix,
|
|
// so we use binary comparison to verify that are exactly the same as the reference.
|
|
// Since to get create the data we use a copy to initialize the reference results, those
|
|
// elements are expected to identical.
|
|
for (j = (std::max)(n,(gtint_t)0); j < ld; j++)
|
|
{
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper) << "This element is expected to not be modified.";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Relative comparison of two matrices with dimensions m-by-n and leading dimension ld, printing variable name.
|
|
*/
|
|
template <typename T>
|
|
void computediff(std::string var_name, char storage, gtint_t m, gtint_t n, T *blis_sol, T *ref_sol, gtint_t ld, double thresh, bool nan_inf_check = false )
|
|
{
|
|
testinghelpers::verification::collect_matrix_data(var_name, blis_sol, ref_sol, m, n, ld, storage);
|
|
|
|
gtint_t i,j;
|
|
ComparisonHelper comp_helper(MATRIX, thresh);
|
|
comp_helper.nan_inf_check = nan_inf_check;
|
|
|
|
// Loop for column-major order
|
|
if( (storage == 'c') || (storage == 'C') )
|
|
{
|
|
for( j = 0 ; j < n ; j++ )
|
|
{
|
|
// First iterate through the elements of the arrays that are part of the matrix
|
|
// and are expected to be modified by a call to BLIS APIs.
|
|
for( i = 0 ; i < m ; i++ )
|
|
{
|
|
comp_helper.i = i;
|
|
comp_helper.j = j;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper);
|
|
}
|
|
// Now iterate through the rest of elements in memory space that are not part of the matrix,
|
|
// so we use binary comparison to verify that are exactly the same as the reference.
|
|
// Since to get create the data we use a copy to initialize the reference results, those
|
|
// elements are expected to identical.
|
|
comp_helper.binary_comparison = true;
|
|
for (i = (std::max)(m,(gtint_t)0); i < ld; i++)
|
|
{
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper) << "This element is expected to not be modified.";
|
|
}
|
|
// Disable binary comparison before we go through the next column.
|
|
comp_helper.binary_comparison = false;
|
|
}
|
|
}
|
|
// Loop for row-major order
|
|
else
|
|
{
|
|
for( i = 0 ; i < m ; i++ )
|
|
{
|
|
// First iterate through the elements of the arrays that are part of the matrix
|
|
// and are expected to be modified by a call to BLIS APIs.
|
|
for( j = 0 ; j < n ; j++ )
|
|
{
|
|
comp_helper.i = i;
|
|
comp_helper.j = j;
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper);
|
|
}
|
|
// Now iterate through the rest of elements in memory space that are not part of the matrix,
|
|
// so we use binary comparison to verify that are exactly the same as the reference.
|
|
// Since to get create the data we use a copy to initialize the reference results, those
|
|
// elements are expected to identical.
|
|
comp_helper.binary_comparison = true;
|
|
for (j = (std::max)(n,(gtint_t)0); j < ld; j++)
|
|
{
|
|
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper) << "This element is expected to not be modified.";
|
|
}
|
|
// Disable binary comparison before we go through the next column.
|
|
comp_helper.binary_comparison = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generic comparison of integer numbers, printing variable name:
|
|
template<typename T>
|
|
testing::AssertionResult EqualityComparison(const char* var_name_char,
|
|
const char* blis_sol_char,
|
|
const char* ref_sol_char,
|
|
const char* comp_helper_char,
|
|
std::string var_name,
|
|
const T blis_sol,
|
|
const T ref_sol,
|
|
const ComparisonHelper comp_helper)
|
|
{
|
|
|
|
if (blis_sol == ref_sol) return testing::AssertionSuccess();
|
|
|
|
// Lazy string construction - only create when actually needed
|
|
auto create_error_message = [&]() -> std::string {
|
|
std::string error_message = var_name_char;
|
|
error_message += " = " + var_name + ", ";
|
|
error_message += blis_sol_char;
|
|
error_message += " = " + testinghelpers::to_string(blis_sol) + ", ";
|
|
error_message += ref_sol_char;
|
|
error_message += " = " + testinghelpers::to_string(ref_sol);
|
|
return error_message;
|
|
};
|
|
|
|
return testing::AssertionFailure() << create_error_message();
|
|
}
|
|
|
|
/**
|
|
* Comparison of two integers, printing variable name.
|
|
*/
|
|
template <>
|
|
inline void computediff<gtint_t>( std::string var_name, gtint_t blis_sol, gtint_t ref_sol, bool nan_inf_check )
|
|
{
|
|
testinghelpers::verification::collect_vector_data(var_name, &blis_sol, &ref_sol, 1);
|
|
|
|
ComparisonHelper comp_helper(SCALAR);
|
|
ASSERT_PRED_FORMAT4(EqualityComparison<gtint_t>, var_name, blis_sol, ref_sol, comp_helper);
|
|
}
|
|
|
|
/**
|
|
* Comparison of two characters, printing variable name.
|
|
*/
|
|
template <>
|
|
inline void computediff<char>( std::string var_name, char blis_sol, char ref_sol, bool nan_inf_check )
|
|
{
|
|
// Widen char to gtint_t for safe CRC calculation (avoids reading beyond 1-byte boundary)
|
|
gtint_t blis_val = static_cast<gtint_t>(blis_sol);
|
|
gtint_t ref_val = static_cast<gtint_t>(ref_sol);
|
|
testinghelpers::verification::collect_vector_data(var_name, &blis_val, &ref_val, 1);
|
|
|
|
ComparisonHelper comp_helper(SCALAR);
|
|
ASSERT_PRED_FORMAT4(EqualityComparison<char>, var_name, blis_sol, ref_sol, comp_helper);
|
|
} |