Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

839 lines
32 KiB
C++

/*******************************************************************************
*
* MIT License
*
* Copyright (C) 2021-2025 Advanced Micro Devices, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*******************************************************************************/
#ifndef ROCWMMA_TEST_COMMON_HPP
#define ROCWMMA_TEST_COMMON_HPP
#if ROCWMMA_TESTS_NO_HALF
#warning("Building tests with hfloat16_t requires !HIP_NO_HALF && !__HIP_NO_HALF_CONVERSIONS__. Proceeding without hfloat16_t")
#endif // !ROCWMMA_NO_HALF && __HIP_NO_HALF_CONVERSIONS__
#include <iostream>
#include <mutex>
#include <tuple>
#include <type_traits>
#include <vector>
#include <rocwmma/internal/types.hpp>
#include "test_config.hpp"
#include "device/common.hpp"
#ifndef CHECK_HIP_ERROR
#define CHECK_HIP_ERROR(expression) \
if(auto status = (expression); status != hipSuccess) \
{ \
fprintf(stderr, \
"hip error: '%s'(%d) at %s:%d\n", \
hipGetErrorString(status), \
status, \
__FILE__, \
__LINE__); \
exit(EXIT_FAILURE); \
}
#endif
#if ROCWMMA_BENCHMARK_TESTS
#ifndef CHECK_AMDSMI_ERROR
#define CHECK_AMDSMI_ERROR(expression, smiErrorFlag) \
if(auto status = (expression); status != AMDSMI_STATUS_SUCCESS) \
{ \
const char* errName; \
amdsmi_status_code_to_string(status, &errName); \
fprintf(stderr, "amdsmi error: '%s'(%d) at %s:%d\n", errName, status, __FILE__, __LINE__); \
smiErrorFlag = true; \
}
#endif
#endif // ROCWMMA_BENCHMARK_TESTS
namespace rocwmma
{
ROCWMMA_DEVICE inline bool isFirstThread()
{
return (threadIdx.x == 0) && (threadIdx.y == 0) && (threadIdx.z == 0) && (blockIdx.x == 0)
&& (blockIdx.y == 0) && (blockIdx.z == 0);
}
template <typename Lhs, typename Rhs>
ROCWMMA_DEVICE bool
expectEqual(Lhs&& testL, Rhs&& testR, bool debugOnFail, const char* file, uint32_t line)
{
bool compare_result = (testL == testR);
if(!compare_result && debugOnFail && isFirstThread())
{
printf("<TestBegin>\n");
printf("File: %s L:%d\n", file, line);
if constexpr(is_same_v<decay_t<Lhs>, hfloat16_t>)
{
printf("Lhs: %f, Rhs: %f\n", static_cast<float>(testL), static_cast<float>(testR));
}
printf("FAILED");
printf("<Test End>\n");
}
return compare_result;
}
template <typename Lhs, typename Rhs>
ROCWMMA_DEVICE bool
expectNotEqual(Lhs&& testL, Rhs&& testR, bool debugOnFail, const char* file, uint32_t line)
{
bool compare_result = (testL != testR);
if(!compare_result && debugOnFail && isFirstThread())
{
printf("<TestBegin>\n");
printf("File: %s L:%d\n", file, line);
printf("FAILED");
printf("<Test End>\n");
}
return compare_result;
}
static constexpr uint32_t ERROR_VALUE = 7u;
static constexpr uint32_t SUCCESS_VALUE = 0u;
namespace quirks
{
// rocBLAS does not yet support Ti/To/Tc = bf16/bf16/bf16
template <typename InputT, typename OutputT, typename ComputeT>
struct rocblas_supported : std::true_type
{
};
template <>
struct rocblas_supported<bfloat16_t, bfloat16_t, bfloat16_t> : std::false_type
{
};
template <>
struct rocblas_supported<int8_t, int8_t, int32_t> : std::false_type
{
};
#if !defined(ROCBLAS_DATA_TYPE_FLOAT8)
template <>
struct rocblas_supported<float8_fnuz_t, float32_t, float32_t> : std::false_type
{
};
template <>
struct rocblas_supported<bfloat8_fnuz_t, float32_t, float32_t> : std::false_type
{
};
#endif
template <>
struct rocblas_supported<float8_t, float32_t, float32_t> : std::false_type
{
};
template <>
struct rocblas_supported<bfloat8_t, float32_t, float32_t> : std::false_type
{
};
} // namespace quirks
template <typename Layout>
struct MatrixUtil
{
template <typename DataT>
__host__ static inline void
print(DataT const* mat, uint32_t m, uint32_t n, std::ostream& stream = std::cout)
{
auto rowMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return row * ld + col; };
auto colMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return col * ld + row; };
auto index = std::is_same<Layout, row_major>::value ? rowMjr : colMjr;
auto ld = std::is_same<Layout, row_major>::value ? n : m;
for(int i = 0; i < m; ++i) // row
{
stream << "[ ";
for(int j = 0; j < n; ++j) // col
{
// (Row, col)
stream << mat[index(i, j, ld)] << " ";
}
stream << "]\n";
}
stream << "\n";
}
template <typename DataT>
__host__ static inline void print(std::vector<DataT> const& mat,
uint32_t m,
uint32_t n,
std::ostream& stream = std::cout)
{
assert(mat.size() == n * m);
print(mat.data(), m, n, stream);
}
template <typename DataT, typename PrintT>
__host__ static inline void
printAsType(DataT const* mat, uint32_t m, uint32_t n, std::ostream& stream = std::cout)
{
auto rowMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return row * ld + col; };
auto colMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return col * ld + row; };
auto index = std::is_same<Layout, row_major>::value ? rowMjr : colMjr;
auto ld = std::is_same<Layout, row_major>::value ? n : m;
for(int i = 0; i < m; ++i) // row
{
stream << "[ ";
for(int j = 0; j < n; ++j) // col
{
// (Row, col)
stream << static_cast<PrintT>(mat[index(i, j, ld)]) << " ";
}
stream << "]\n";
}
stream << "\n";
}
template <typename DataT>
__host__ static inline void fillWithPadding(
DataT* mat, uint32_t m, uint32_t n, uint32_t padM, uint32_t padN, DataT padValue)
{
auto rowMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return row * ld + col; };
auto colMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return col * ld + row; };
const auto limitM = m + 2 * padM;
const auto limitN = n + 2 * padN;
auto index = std::is_same<Layout, row_major>::value ? rowMjr : colMjr;
auto ld = std::is_same<Layout, row_major>::value ? limitN : limitM;
#pragma omp parallel for
for(int i = 0; i < limitM; ++i) // row
{
#pragma omp parallel for
for(int j = 0; j < limitN; ++j) // col
{
auto idx = index(i, j, ld);
if(i < padM || i >= (limitM - padM) || j < padN || j >= (limitN - padN))
{
mat[idx] = padValue;
}
else
{
// Count up in integers, in ascending order for each row.
auto value = ((i - padM) * n + (j - padN)) % 5;
mat[idx] = ((value % 3) && std::is_signed<DataT>::value)
? -static_cast<DataT>(value)
: static_cast<DataT>(value);
}
}
}
}
template <typename DataT>
__host__ static inline void fillWithPadding(std::vector<DataT>& mat,
uint32_t m,
uint32_t n,
uint32_t padM,
uint32_t padN,
DataT padValue)
{
assert(mat.size() == n * m);
fillWithPadding(mat.data(), m, n, padM, padN, padValue);
}
template <typename DataT>
__host__ static inline void fillWithPaddingLaunchKernel(
DataT* d_mat, uint32_t m, uint32_t n, uint32_t padM, uint32_t padN, DataT padValue)
{
const auto limitM = m + 2 * padM;
const auto limitN = n + 2 * padN;
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceil_div(limitM * limitN, blockDim.x), 1, 1);
hipLaunchKernelGGL((fillWithPaddingKernel<DataT, Layout>),
gridDim,
blockDim,
0,
0,
d_mat,
m,
n,
padM,
padN,
padValue);
}
template <typename DataT>
__host__ static inline void fill(DataT* mat, uint32_t m, uint32_t n)
{
auto rowMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return row * ld + col; };
auto colMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return col * ld + row; };
auto index = std::is_same<Layout, row_major>::value ? rowMjr : colMjr;
auto ld = std::is_same<Layout, row_major>::value ? n : m;
#pragma omp parallel for
for(int i = 0; i < m; ++i) // row
{
#pragma omp parallel for
for(int j = 0; j < n; ++j) // col
{
// Count up in integers, in ascending order for each row.
auto value = (i * n + j) % 5;
auto idx = index(i, j, ld);
mat[idx] = ((value % 3) && std::is_signed<DataT>::value)
? -static_cast<DataT>(value)
: static_cast<DataT>(value);
}
}
}
template <typename DataT>
__host__ static inline void fill(std::vector<DataT>& mat, uint32_t m, uint32_t n)
{
assert(mat.size() == n * m);
fill(mat.data(), m, n);
}
template <typename DataT>
__host__ static inline void fillVal(DataT* mat, uint32_t m, uint32_t n, DataT value)
{
#pragma omp parallel for
for(int i = 0; i < m * n; ++i) // row
{
mat[i] = value;
}
}
template <typename DataT>
__host__ static inline void
fillVal(std::vector<DataT>& mat, uint32_t m, uint32_t n, DataT value)
{
assert(mat.size() == n * m);
fillVal(mat.data(), m, n, value);
}
// fill kernel wrapper for M x N matrix
template <typename DataT>
__host__ static inline void fillLaunchKernel(DataT* d_mat, uint32_t m, uint32_t n)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceil_div(m * n, blockDim.x), 1, 1);
hipLaunchKernelGGL((fillKernel<DataT, Layout>), gridDim, blockDim, 0, 0, d_mat, m, n);
}
// fill kernel wrapper for batched M x K matrices
template <typename DataT>
__host__ static inline void
fillLaunchKernel(DataT* d_mat, uint32_t m, uint32_t k, uint32_t b)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceil_div(m * k, blockDim.x), 1, b);
hipLaunchKernelGGL(
(fillKernel<DataT, Layout>), gridDim, blockDim, 0, 0, d_mat, m, k, b);
}
// fill kernel wrapper for M x N matrix for a specific value
template <typename DataT>
__host__ static inline void
fillValLaunchKernel(DataT* d_mat, uint32_t m, uint32_t n, DataT value)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceil_div(m * n, blockDim.x), 1, 1);
hipLaunchKernelGGL(
(fillValKernel<DataT, Layout>), gridDim, blockDim, 0, 0, d_mat, m, n, value);
}
// fill kernel wrapper for M x N matrix for mat[i] = i
template <typename DataT>
__host__ static inline void fillIdxLaunchKernel(DataT* d_mat, uint32_t m, uint32_t n)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceil_div(m * n, blockDim.x), 1, 1);
hipLaunchKernelGGL(
(fillIdxKernel<DataT, Layout>), gridDim, blockDim, 0, 0, d_mat, m, n);
}
};
// compareEqual on two different layouts: must calculate index offsets
template <typename TypeA,
typename TypeB,
typename LayoutA,
typename LayoutB,
typename std::enable_if_t<!std::is_same<LayoutA, LayoutB>::value, int> = 0>
std::pair<bool, double> compareEqual(TypeA const* matrixA,
TypeB const* matrixB,
uint32_t m,
uint32_t n,
uint32_t lda,
uint32_t ldb,
double tolerance = 10.0)
{
bool retval = true;
double max_relative_error = 0.0;
// Some types don't have direct conversion to double.
// Convert to float first then to double.
auto toDoubleA
= [](TypeA const& val) { return static_cast<double>(static_cast<float>(val)); };
auto toDoubleB
= [](TypeB const& val) { return static_cast<double>(static_cast<float>(val)); };
auto rowMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return row * ld + col; };
auto colMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return col * ld + row; };
auto indexA = std::is_same<LayoutA, row_major>::value ? rowMjr : colMjr;
auto indexB = std::is_same<LayoutB, row_major>::value ? rowMjr : colMjr;
bool isInf = false;
bool isNaN = false;
std::mutex writeMutex;
#pragma omp parallel for
for(int i = 0; i < m; ++i) // Row
{
#pragma omp parallel for
for(int j = 0; j < n; ++j) // Col
{
auto valA = matrixA[indexA(i, j, lda)];
auto valB = matrixB[indexB(i, j, ldb)];
auto numerator = fabs(toDoubleA(valA) - toDoubleB(valB));
auto divisor = fabs(toDoubleA(valA)) + fabs(toDoubleB(valB)) + 1.0;
if(std::isinf(numerator) || std::isinf(divisor))
{
#pragma omp atomic
isInf |= true;
}
else
{
auto relative_error = numerator / divisor;
if(std::isnan(relative_error))
{
#pragma omp atomic
isNaN |= true;
}
else if(relative_error > max_relative_error)
{
const std::lock_guard<std::mutex> guard(writeMutex);
// Double check in case of stall
if(relative_error > max_relative_error)
{
max_relative_error = relative_error;
}
}
}
if(isInf || isNaN)
{
i = m;
j = n;
}
}
}
auto eps = toDoubleA(std::numeric_limits<TypeA>::epsilon());
if(isInf)
{
retval = false;
max_relative_error = std::numeric_limits<TypeA>::infinity();
}
else if(isNaN)
{
retval = false;
max_relative_error = double(std::numeric_limits<TypeA>::signaling_NaN());
}
else if(max_relative_error > (eps * tolerance))
{
retval = false;
}
return std::make_pair(retval, max_relative_error);
}
// compareEqual on two equal layouts: index offsets are identical
// can use slightly faster 1D compare
template <typename TypeA,
typename TypeB,
typename LayoutA,
typename LayoutB,
typename std::enable_if_t<std::is_same<LayoutA, LayoutB>::value, int> = 0>
std::pair<bool, double> compareEqual(TypeA const* matrixA,
TypeB const* matrixB,
uint32_t m,
uint32_t n,
uint32_t lda,
uint32_t ldb,
double tolerance = 10.0)
{
assert(lda == ldb && "Leading dims must match");
bool retval = true;
double max_relative_error = 0.0;
// Some types don't have direct conversion to double.
// Convert to float first then to double.
auto toDoubleA
= [](TypeA const& val) { return static_cast<double>(static_cast<float>(val)); };
auto toDoubleB
= [](TypeB const& val) { return static_cast<double>(static_cast<float>(val)); };
bool isInf = false;
bool isNaN = false;
std::mutex writeMutex;
#pragma omp parallel for
for(int i = 0; i < m * n; ++i) // Row
{
auto valA = matrixA[i];
auto valB = matrixB[i];
auto numerator = fabs(toDoubleA(valA) - toDoubleB(valB));
auto divisor = fabs(toDoubleA(valA)) + fabs(toDoubleB(valB)) + 1.0;
if(std::isinf(numerator) || std::isinf(divisor))
{
#pragma omp atomic
isInf |= true;
}
else
{
auto relative_error = numerator / divisor;
if(std::isnan(relative_error))
{
#pragma omp atomic
isNaN |= true;
}
else if(relative_error > max_relative_error)
{
const std::lock_guard<std::mutex> guard(writeMutex);
// Double check in case of stall
if(relative_error > max_relative_error)
{
max_relative_error = relative_error;
}
}
}
if(isInf || isNaN)
{
i = m * n;
}
}
auto eps = toDoubleA(std::numeric_limits<TypeA>::epsilon());
if(isInf)
{
retval = false;
max_relative_error = std::numeric_limits<TypeA>::infinity();
}
else if(isNaN)
{
retval = false;
max_relative_error = double(std::numeric_limits<TypeA>::signaling_NaN());
}
else if(max_relative_error > (eps * tolerance))
{
retval = false;
}
return std::make_pair(retval, max_relative_error);
}
template <typename TypeA, typename TypeB, typename LayoutA, typename LayoutB>
inline std::pair<bool, double> compareEqual(
TypeA const* matrixA, TypeB const* matrixB, uint32_t m, uint32_t n, double tolerance = 10.0)
{
uint32_t lda = std::is_same<LayoutA, row_major>::value ? n : m;
uint32_t ldb = std::is_same<LayoutB, row_major>::value ? n : m;
return compareEqual<TypeA, TypeB, LayoutA, LayoutB>(
matrixA, matrixB, m, n, lda, ldb, tolerance);
}
template <typename TypeA, typename TypeB, typename LayoutA, typename LayoutB>
inline std::pair<bool, double> compareEqual(std::vector<TypeA> const& a,
std::vector<TypeB> const& b,
uint32_t m,
uint32_t n,
double tolerance = 10.0)
{
assert(a.size() == b.size() && "A and B are not the same size");
assert(a.size() == m * n && "A and B do not match size M x N");
return compareEqual<TypeA, TypeB, LayoutA, LayoutB>(a.data(), b.data(), m, n, tolerance);
}
template <typename TypeA, typename TypeB, typename LayoutA, typename LayoutB>
inline std::pair<bool, double> compareEqual(std::vector<TypeA> const& a,
std::vector<TypeB> const& b,
uint32_t m,
uint32_t n,
uint32_t lda,
uint32_t ldb,
double tolerance = 10.0)
{
assert(a.size() == b.size() && "A and B are not the same size");
assert(a.size() == m * n && "A and B do not match size m x n");
return compareEqual<TypeA, TypeB, LayoutA, LayoutB>(
a.data(), b.data(), m, n, lda, ldb, tolerance);
}
// compareEqual kernel wrapper for gemm tests
template <typename TypeA, typename TypeB, typename LayoutA, typename LayoutB>
std::pair<bool, double> compareEqualLaunchKernel(
TypeA* matrixA, TypeB* matrixB, uint32_t m, uint32_t n, double tolerance = 10.0)
{
uint32_t lda = std::is_same<LayoutA, row_major>::value ? n : m;
uint32_t ldb = std::is_same<LayoutB, row_major>::value ? n : m;
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceil_div(m * n, blockDim.x), 1, 1);
double* d_relativeError;
double maxRelativeError;
CHECK_HIP_ERROR(hipMalloc(&d_relativeError, m * n * sizeof(double)));
hipEvent_t syncEvent;
CHECK_HIP_ERROR(hipEventCreate(&syncEvent));
// Calculate the relative error for each element of matrix A/B
hipLaunchKernelGGL((compareEqualKernel<TypeA, TypeB, LayoutA, LayoutB>),
gridDim,
blockDim,
0,
0,
matrixA,
matrixB,
d_relativeError,
m,
n,
lda,
ldb);
CHECK_HIP_ERROR(hipEventRecord(syncEvent));
CHECK_HIP_ERROR(hipEventSynchronize(syncEvent));
// Determine the maximum relative error
blockDim = dim3(512, 1, 1);
uint32_t maxElements = 1024;
uint32_t offset = 1;
for(uint32_t i = m * n; i > 1; i = ceil_div(i, maxElements))
{
gridDim = dim3(ceil_div(i, maxElements), 1, 1);
auto elements = i > maxElements ? maxElements : i;
hipLaunchKernelGGL((maxReduceKernel),
gridDim,
blockDim,
0,
0,
d_relativeError,
elements,
offset,
m * n);
CHECK_HIP_ERROR(hipEventRecord(syncEvent));
CHECK_HIP_ERROR(hipEventSynchronize(syncEvent));
offset = offset * maxElements;
}
CHECK_HIP_ERROR(
hipMemcpy(&maxRelativeError, d_relativeError, sizeof(double), hipMemcpyDeviceToHost));
CHECK_HIP_ERROR(hipEventDestroy(syncEvent));
// Free allocated device memory
CHECK_HIP_ERROR(hipFree(d_relativeError));
bool retval = true;
bool isNaN = std::isnan(maxRelativeError);
auto toDoubleA
= [](TypeA const& val) { return static_cast<double>(static_cast<float>(val)); };
auto eps = toDoubleA(std::numeric_limits<TypeA>::epsilon());
if(isNaN)
{
retval = false;
maxRelativeError = double(std::numeric_limits<TypeA>::signaling_NaN());
}
else if(maxRelativeError > (eps * tolerance))
{
retval = false;
}
return std::make_pair(retval, maxRelativeError);
}
// compareEqual kernel wrapper for batched matrices
template <typename TypeA, typename TypeB>
std::pair<bool, double> compareEqualLaunchKernel(
TypeA* matrixA, TypeB* matrixB, uint32_t m, uint32_t k, uint32_t b, double tolerance = 10.0)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceil_div(m * k, blockDim.x), 1, b);
double* d_relativeError;
double maxRelativeError;
CHECK_HIP_ERROR(hipMalloc(&d_relativeError, m * k * b * sizeof(double)));
hipEvent_t syncEvent;
CHECK_HIP_ERROR(hipEventCreate(&syncEvent));
// Calculate the relative error for each element of matrix A/B
hipLaunchKernelGGL((compareEqualKernel<TypeA, TypeB>),
gridDim,
blockDim,
0,
0,
matrixA,
matrixB,
d_relativeError,
m,
k,
b);
CHECK_HIP_ERROR(hipEventRecord(syncEvent));
CHECK_HIP_ERROR(hipEventSynchronize(syncEvent));
// Determine the maximum relative error
blockDim = dim3(512, 1, 1);
uint32_t maxElements = 1024;
uint32_t offset = 1;
for(uint32_t i = m * k * b; i > 1; i = ceil_div(i, maxElements))
{
gridDim = dim3(ceil_div(i, maxElements), 1, 1);
auto elements = i > maxElements ? maxElements : i;
hipLaunchKernelGGL((maxReduceKernel),
gridDim,
blockDim,
0,
0,
d_relativeError,
elements,
offset,
m * k * b);
CHECK_HIP_ERROR(hipEventRecord(syncEvent));
CHECK_HIP_ERROR(hipEventSynchronize(syncEvent));
offset = offset * maxElements;
}
CHECK_HIP_ERROR(
hipMemcpy(&maxRelativeError, d_relativeError, sizeof(double), hipMemcpyDeviceToHost));
CHECK_HIP_ERROR(hipEventDestroy(syncEvent));
// Free allocated device memory
CHECK_HIP_ERROR(hipFree(d_relativeError));
bool retval = true;
bool isNaN = std::isnan(maxRelativeError);
auto toDoubleA
= [](TypeA const& val) { return static_cast<double>(static_cast<float>(val)); };
auto eps = toDoubleA(std::numeric_limits<TypeA>::epsilon());
if(isNaN)
{
retval = false;
maxRelativeError = double(std::numeric_limits<TypeA>::signaling_NaN());
}
else if(maxRelativeError > (eps * tolerance))
{
retval = false;
}
return std::make_pair(retval, maxRelativeError);
}
// Count occurrences of val in the input array
template <typename DataT>
uint64_t countVal(DataT const* a, uint64_t size, DataT const& val, double tolerance = 10.0)
{
uint64_t count = 0;
#pragma omp parallel for
for(uint64_t i = 0; i < size; ++i)
{
using TestT = double;
if(fabs(static_cast<TestT>(val) - static_cast<TestT>(a[i]))
<= (tolerance * static_cast<TestT>(std::numeric_limits<DataT>::epsilon())))
{
#pragma omp atomic
++count;
}
}
return count;
}
// Count occurrences of val inside the rectangular padding of input matrix
template <typename DataT, typename LayoutT>
__host__ static inline uint64_t countPaddingVal(DataT* mat,
uint32_t m,
uint32_t n,
uint32_t padM,
uint32_t padN,
DataT padValue,
double tolerance = 10.0)
{
auto rowMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return row * ld + col; };
auto colMjr = [](uint32_t row, uint32_t col, uint32_t ld) { return col * ld + row; };
const auto limitM = m + 2 * padM;
const auto limitN = n + 2 * padN;
auto index = std::is_same<LayoutT, row_major>::value ? rowMjr : colMjr;
auto ld = std::is_same<LayoutT, row_major>::value ? limitN : limitM;
uint64_t count = 0;
#pragma omp parallel for
for(uint32_t i = 0; i < limitM; ++i) // row
{
#pragma omp parallel for
for(uint32_t j = 0; j < limitN; ++j) // col
{
auto idx = index(i, j, ld);
if(i < padM || i >= (limitM - padM) || j < padN || j >= (limitN - padN))
{
using TestT = float64_t;
if(fabs(static_cast<TestT>(padValue) - static_cast<TestT>(mat[idx]))
<= (tolerance * static_cast<TestT>(std::numeric_limits<DataT>::epsilon())))
{
#pragma omp atomic
++count;
}
}
}
}
return count;
}
} // namespace rocwmma
#endif // ROCWMMA_TEST_COMMON_HPP