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
786 lines
25 KiB
C++
786 lines
25 KiB
C++
/*******************************************************************************
|
|
*
|
|
* MIT License
|
|
*
|
|
* Copyright (C) 2023-2026 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.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#include <ctime>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <math.h>
|
|
#include <mutex>
|
|
#include <numeric>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
// hiptensor includes
|
|
#include <hiptensor/hiptensor.h>
|
|
#include <hiptensor/hiptensor_types.h>
|
|
#include <hiptensor/internal/hiptensor_utility.hpp>
|
|
#include <hiptensor/internal/types.hpp>
|
|
|
|
#include "data_types.hpp"
|
|
#include "device/common.hpp"
|
|
#include "hip_resource.hpp"
|
|
#include "type_traits.hpp"
|
|
|
|
#define HIPTENSOR_FREE_DEVICE(ptr) \
|
|
if(ptr != nullptr) \
|
|
{ \
|
|
CHECK_HIP_ERROR(hipFree(ptr)); \
|
|
}
|
|
|
|
#define HIPTENSOR_FREE_HOST(ptr) \
|
|
if(ptr != nullptr) \
|
|
{ \
|
|
CHECK_HIP_ERROR(hipHostFree(ptr)); \
|
|
}
|
|
|
|
inline double getEpsilon(hiptensorComputeDescriptor_t id)
|
|
{
|
|
auto toDouble = [](auto const& val) { return static_cast<double>(static_cast<float>(val)); };
|
|
|
|
if(id == HIPTENSOR_COMPUTE_DESC_16F)
|
|
{
|
|
return std::pow(2, -10); // F16 machine epsilon (std::numeric_limits<_Float16> isn't valid on Windows MSVC)
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_16BF)
|
|
{
|
|
return std::pow(2, -7); // BF16 machine epsilon (std::numeric_limits<hip_bfloat16> isn't valid on Windows MSVC)
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_32F)
|
|
{
|
|
return toDouble(std::numeric_limits<float>::epsilon());
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_64F)
|
|
{
|
|
return toDouble(std::numeric_limits<double>::epsilon());
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_8U)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_8I)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_32U)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_32I)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_C32F)
|
|
{
|
|
return toDouble(std::numeric_limits<float>::epsilon());
|
|
}
|
|
else if(id == HIPTENSOR_COMPUTE_DESC_C64F)
|
|
{
|
|
return toDouble(std::numeric_limits<double>::epsilon());
|
|
}
|
|
else
|
|
{
|
|
#if !NDEBUG
|
|
std::cout << "Unhandled hiptensorComputeDescriptor_t: " << id << std::endl;
|
|
#endif // !NDEBUG
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
inline bool isF16Supported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx908") != std::string::npos)
|
|
|| (deviceName.find("gfx90a") != std::string::npos)
|
|
|| (deviceName.find("gfx942") != std::string::npos)
|
|
|| (deviceName.find("gfx950") != std::string::npos)
|
|
|| (deviceName.find("gfx1100") != std::string::npos)
|
|
|| (deviceName.find("gfx1101") != std::string::npos)
|
|
|| (deviceName.find("gfx1102") != std::string::npos)
|
|
|| (deviceName.find("gfx1103") != std::string::npos)
|
|
|| (deviceName.find("gfx1150") != std::string::npos)
|
|
|| (deviceName.find("gfx1151") != std::string::npos)
|
|
|| (deviceName.find("gfx1152") != std::string::npos)
|
|
|| (deviceName.find("gfx1153") != std::string::npos)
|
|
|| (deviceName.find("gfx1200") != std::string::npos)
|
|
|| (deviceName.find("gfx1201") != std::string::npos)
|
|
|| (deviceName.find("gfx1250") != std::string::npos);
|
|
}
|
|
|
|
inline bool isF32Supported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx908") != std::string::npos)
|
|
|| (deviceName.find("gfx90a") != std::string::npos)
|
|
|| (deviceName.find("gfx942") != std::string::npos)
|
|
|| (deviceName.find("gfx950") != std::string::npos)
|
|
|| (deviceName.find("gfx1100") != std::string::npos)
|
|
|| (deviceName.find("gfx1101") != std::string::npos)
|
|
|| (deviceName.find("gfx1102") != std::string::npos)
|
|
|| (deviceName.find("gfx1103") != std::string::npos)
|
|
|| (deviceName.find("gfx1150") != std::string::npos)
|
|
|| (deviceName.find("gfx1151") != std::string::npos)
|
|
|| (deviceName.find("gfx1152") != std::string::npos)
|
|
|| (deviceName.find("gfx1153") != std::string::npos)
|
|
|| (deviceName.find("gfx1200") != std::string::npos)
|
|
|| (deviceName.find("gfx1201") != std::string::npos)
|
|
|| (deviceName.find("gfx1250") != std::string::npos);
|
|
}
|
|
|
|
inline bool isF64Supported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx90a") != std::string::npos)
|
|
|| (deviceName.find("gfx942") != std::string::npos)
|
|
|| (deviceName.find("gfx950") != std::string::npos)
|
|
|| (deviceName.find("gfx1250") != std::string::npos);
|
|
}
|
|
|
|
inline bool isF16F16MatrixCoreSupported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx1100") != std::string::npos)
|
|
|| (deviceName.find("gfx1101") != std::string::npos)
|
|
|| (deviceName.find("gfx1102") != std::string::npos)
|
|
|| (deviceName.find("gfx1103") != std::string::npos)
|
|
|| (deviceName.find("gfx1150") != std::string::npos)
|
|
|| (deviceName.find("gfx1151") != std::string::npos)
|
|
|| (deviceName.find("gfx1152") != std::string::npos)
|
|
|| (deviceName.find("gfx1153") != std::string::npos)
|
|
|| (deviceName.find("gfx1200") != std::string::npos)
|
|
|| (deviceName.find("gfx1201") != std::string::npos)
|
|
|| (deviceName.find("gfx1250") != std::string::npos);
|
|
}
|
|
|
|
inline bool isF32F16MatrixCoreSupported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx908") != std::string::npos)
|
|
|| (deviceName.find("gfx90a") != std::string::npos)
|
|
|| (deviceName.find("gfx942") != std::string::npos)
|
|
|| (deviceName.find("gfx950") != std::string::npos)
|
|
|| (deviceName.find("gfx1100") != std::string::npos)
|
|
|| (deviceName.find("gfx1101") != std::string::npos)
|
|
|| (deviceName.find("gfx1102") != std::string::npos)
|
|
|| (deviceName.find("gfx1103") != std::string::npos)
|
|
|| (deviceName.find("gfx1150") != std::string::npos)
|
|
|| (deviceName.find("gfx1151") != std::string::npos)
|
|
|| (deviceName.find("gfx1152") != std::string::npos)
|
|
|| (deviceName.find("gfx1153") != std::string::npos)
|
|
|| (deviceName.find("gfx1200") != std::string::npos)
|
|
|| (deviceName.find("gfx1201") != std::string::npos)
|
|
|| (deviceName.find("gfx1250") != std::string::npos);
|
|
}
|
|
|
|
inline bool isF32F32MatrixCoreSupported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx908") != std::string::npos)
|
|
|| (deviceName.find("gfx90a") != std::string::npos)
|
|
|| (deviceName.find("gfx942") != std::string::npos)
|
|
|| (deviceName.find("gfx950") != std::string::npos)
|
|
|| (deviceName.find("gfx1250") != std::string::npos);
|
|
}
|
|
|
|
inline bool isF16F32MatrixCoreSupported()
|
|
{
|
|
return isF32F32MatrixCoreSupported();
|
|
}
|
|
|
|
inline bool isF64F64MatrixCoreSupported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx90a") != std::string::npos)
|
|
|| (deviceName.find("gfx942") != std::string::npos)
|
|
|| (deviceName.find("gfx950") != std::string::npos);
|
|
}
|
|
|
|
inline bool isF64F32MatrixCoreSupported()
|
|
{
|
|
hipDevice_t mHandle;
|
|
hipDeviceProp_t mProps;
|
|
|
|
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
|
|
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
|
|
|
|
std::string deviceName(mProps.gcnArchName);
|
|
|
|
return (deviceName.find("gfx90a") != std::string::npos)
|
|
|| (deviceName.find("gfx942") != std::string::npos)
|
|
|| (deviceName.find("gfx950") != std::string::npos)
|
|
|| (deviceName.find("gfx1250") != std::string::npos);
|
|
}
|
|
|
|
inline bool isDataType16Bits(hiptensorDataType_t dataType)
|
|
{
|
|
return (dataType == HIPTENSOR_R_16F || dataType == HIPTENSOR_R_16BF
|
|
|| dataType == HIPTENSOR_C_16F || dataType == HIPTENSOR_C_16BF);
|
|
}
|
|
|
|
inline bool isDataType32Bits(hiptensorDataType_t dataType)
|
|
{
|
|
return (dataType == HIPTENSOR_R_32F || dataType == HIPTENSOR_C_32F);
|
|
}
|
|
|
|
inline bool isDataType64Bits(hiptensorDataType_t dataType)
|
|
{
|
|
return (dataType == HIPTENSOR_R_64F || dataType == HIPTENSOR_C_64F);
|
|
}
|
|
|
|
inline bool isDataTypeComplex(hiptensorDataType_t dataType)
|
|
{
|
|
return (dataType == HIPTENSOR_C_32F || dataType == HIPTENSOR_C_64F
|
|
|| dataType == HIPTENSOR_C_16F || dataType == HIPTENSOR_C_16BF);
|
|
}
|
|
|
|
template <typename intT1,
|
|
class = typename std::enable_if<std::is_integral<intT1>::value>::type,
|
|
typename intT2,
|
|
class = typename std::enable_if<std::is_integral<intT2>::value>::type>
|
|
static constexpr intT1 ceilDiv(const intT1 numerator, const intT2 divisor)
|
|
{
|
|
return (numerator + divisor - 1) / divisor;
|
|
}
|
|
|
|
template <typename Container>
|
|
auto getProduct(const Container& container,
|
|
typename Container::value_type init = typename Container::value_type{1})
|
|
{
|
|
return std::accumulate(std::begin(container),
|
|
std::end(container),
|
|
init,
|
|
std::multiplies<typename Container::value_type>{});
|
|
}
|
|
|
|
// fill kernel for 'elementSize' elements
|
|
template <typename DataType>
|
|
__host__ static inline void fillLaunchKernel(DataType* data, uint32_t elementSize, uint32_t seed)
|
|
{
|
|
auto blockDim = dim3(1024, 1, 1);
|
|
auto gridDim = dim3(ceilDiv(elementSize, blockDim.x), 1, 1);
|
|
hipLaunchKernelGGL((fillKernel<DataType>), gridDim, blockDim, 0, 0, data, elementSize, seed);
|
|
}
|
|
|
|
// fill kernel for 'elementSize' elements with positive values
|
|
template <typename DataType>
|
|
__host__ static inline void
|
|
fillPositiveValLaunchKernel(DataType* data, uint32_t elementSize, uint32_t seed)
|
|
{
|
|
auto blockDim = dim3(1024, 1, 1);
|
|
auto gridDim = dim3(ceilDiv(elementSize, blockDim.x), 1, 1);
|
|
hipLaunchKernelGGL(
|
|
(fillPositiveValKernel<DataType>), gridDim, blockDim, 0, 0, data, elementSize, seed);
|
|
}
|
|
|
|
// fill kernel wrapper for 'elementSize' elements with a specific value
|
|
template <typename DataType>
|
|
__host__ static inline void
|
|
fillValLaunchKernel(DataType* data, uint32_t elementSize, DataType value)
|
|
{
|
|
auto blockDim = dim3(1024, 1, 1);
|
|
auto gridDim = dim3(ceilDiv(elementSize, blockDim.x), 1, 1);
|
|
hipLaunchKernelGGL(
|
|
(fillValKernel<DataType>), gridDim, blockDim, 0, 0, data, elementSize, value);
|
|
}
|
|
|
|
template <typename DDataType>
|
|
std::pair<bool, double> compareEqual(DDataType const* deviceD,
|
|
DDataType const* hostD,
|
|
std::size_t elementsD,
|
|
hiptensorComputeDescriptor_t computeType,
|
|
double tolerance = 0.0)
|
|
{
|
|
bool retval = true;
|
|
double max_relative_error = 0.0;
|
|
|
|
auto toDouble
|
|
= [](DDataType 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 < elementsD; ++i)
|
|
{
|
|
auto valDevice = deviceD[i];
|
|
auto valHost = hostD[i];
|
|
|
|
auto numerator = fabs(toDouble(valDevice) - toDouble(valHost));
|
|
auto divisor = fabs(toDouble(valDevice)) + fabs(toDouble(valHost)) + 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 = elementsD;
|
|
}
|
|
}
|
|
|
|
if(tolerance == 0.0)
|
|
{
|
|
// use the same default tolerance value as CK
|
|
if(computeType == HIPTENSOR_COMPUTE_DESC_16BF
|
|
|| std::is_same_v<DDataType, hiptensor::bfloat16_t>)
|
|
{
|
|
const double epsilon = std::pow(2, -7);
|
|
tolerance = epsilon * 2;
|
|
}
|
|
else if(computeType == HIPTENSOR_COMPUTE_DESC_16F
|
|
|| std::is_same_v<DDataType, hiptensor::float16_t>)
|
|
{
|
|
const double epsilon = std::pow(2, -10);
|
|
tolerance = epsilon * 2;
|
|
}
|
|
else
|
|
{
|
|
tolerance = 1e-5;
|
|
}
|
|
}
|
|
|
|
if(isInf)
|
|
{
|
|
retval = false;
|
|
max_relative_error = std::numeric_limits<DDataType>::infinity();
|
|
}
|
|
else if(isNaN)
|
|
{
|
|
retval = false;
|
|
max_relative_error = std::numeric_limits<DDataType>::signaling_NaN();
|
|
}
|
|
else if(max_relative_error > tolerance)
|
|
{
|
|
retval = false;
|
|
}
|
|
|
|
return std::make_pair(retval, max_relative_error);
|
|
}
|
|
|
|
template <typename DDataType>
|
|
std::pair<bool, double> compareEqualLaunchKernel(DDataType* deviceD,
|
|
DDataType* hostD,
|
|
std::size_t elementsD,
|
|
hiptensorComputeDescriptor_t computeType,
|
|
double tolerance = 0.0)
|
|
{
|
|
static hiptensor::HipResource::DevicePtrT relativeErrorPtr;
|
|
static std::size_t relativeErrorSize = 0;
|
|
|
|
auto blockDim = dim3(1024, 1, 1);
|
|
auto gridDim = dim3(ceilDiv(elementsD, blockDim.x), 1, 1);
|
|
|
|
double maxRelativeError;
|
|
|
|
std::size_t requiredErrorSize = elementsD * sizeof(double);
|
|
if(requiredErrorSize > relativeErrorSize)
|
|
{
|
|
hiptensor::HipResource::reallocDevice(relativeErrorPtr, requiredErrorSize);
|
|
relativeErrorSize = requiredErrorSize;
|
|
}
|
|
|
|
hipEvent_t syncEvent;
|
|
CHECK_HIP_ERROR(hipEventCreate(&syncEvent));
|
|
|
|
// Calculate the relative error for each element of Tensor D
|
|
hipLaunchKernelGGL((compareEqualKernel<DDataType>),
|
|
gridDim,
|
|
blockDim,
|
|
0,
|
|
0,
|
|
deviceD,
|
|
hostD,
|
|
static_cast<double*>(relativeErrorPtr.get()),
|
|
elementsD);
|
|
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 = elementsD; i > 1; i = ceilDiv(i, maxElements))
|
|
{
|
|
gridDim = dim3(ceilDiv(i, maxElements), 1, 1);
|
|
auto elements = i > maxElements ? maxElements : i;
|
|
|
|
hipLaunchKernelGGL((maxReduceKernel),
|
|
gridDim,
|
|
blockDim,
|
|
0,
|
|
0,
|
|
static_cast<double*>(relativeErrorPtr.get()),
|
|
elements,
|
|
offset,
|
|
elementsD);
|
|
|
|
CHECK_HIP_ERROR(hipEventRecord(syncEvent));
|
|
CHECK_HIP_ERROR(hipEventSynchronize(syncEvent));
|
|
offset = offset * maxElements;
|
|
}
|
|
CHECK_HIP_ERROR(hipEventDestroy(syncEvent));
|
|
|
|
CHECK_HIP_ERROR(hipMemcpy(&maxRelativeError,
|
|
static_cast<double*>(relativeErrorPtr.get()),
|
|
sizeof(double),
|
|
hipMemcpyDeviceToHost));
|
|
|
|
bool retval = true;
|
|
bool isNaN = std::isnan(maxRelativeError);
|
|
|
|
auto toDouble
|
|
= [](DDataType const& val) { return static_cast<double>(static_cast<float>(val)); };
|
|
|
|
if(tolerance == 0.0)
|
|
{
|
|
// use the same default tolerance value as CK
|
|
if(computeType == HIPTENSOR_COMPUTE_DESC_16BF
|
|
|| std::is_same_v<DDataType, hiptensor::bfloat16_t>)
|
|
{
|
|
const double epsilon = std::pow(2, -7);
|
|
tolerance = epsilon * 2;
|
|
}
|
|
else if(computeType == HIPTENSOR_COMPUTE_DESC_16F
|
|
|| std::is_same_v<DDataType, hiptensor::float16_t>)
|
|
{
|
|
const double epsilon = std::pow(2, -10);
|
|
tolerance = epsilon * 2;
|
|
}
|
|
else
|
|
{
|
|
tolerance = 1e-5;
|
|
}
|
|
}
|
|
|
|
if(isNaN)
|
|
{
|
|
retval = false;
|
|
maxRelativeError = std::numeric_limits<DDataType>::signaling_NaN();
|
|
}
|
|
else if(maxRelativeError > (tolerance))
|
|
{
|
|
retval = false;
|
|
}
|
|
|
|
return std::make_pair(retval, maxRelativeError);
|
|
}
|
|
|
|
namespace hiptensor
|
|
{
|
|
template <typename T>
|
|
struct has_const_iterator
|
|
{
|
|
private:
|
|
typedef char yes;
|
|
typedef struct
|
|
{
|
|
char array[2];
|
|
} no;
|
|
|
|
template <typename C>
|
|
static yes test(typename C::const_iterator*);
|
|
template <typename C>
|
|
static no test(...);
|
|
|
|
public:
|
|
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
|
|
typedef T type;
|
|
};
|
|
|
|
template <typename T>
|
|
struct has_begin_end
|
|
{
|
|
template <typename C>
|
|
static char (&f(
|
|
typename std::enable_if<std::is_same<decltype(static_cast<typename C::const_iterator (
|
|
C::*)() const>(&C::begin)),
|
|
typename C::const_iterator (C::*)() const>::value,
|
|
void>::type*))[1];
|
|
|
|
template <typename C>
|
|
static char (&f(...))[2];
|
|
|
|
template <typename C>
|
|
static char (&g(
|
|
typename std::enable_if<std::is_same<decltype(static_cast<typename C::const_iterator (
|
|
C::*)() const>(&C::end)),
|
|
typename C::const_iterator (C::*)() const>::value,
|
|
void>::type*))[1];
|
|
|
|
template <typename C>
|
|
static char (&g(...))[2];
|
|
|
|
static bool const beg_value = sizeof(f<T>(0)) == 1;
|
|
static bool const end_value = sizeof(g<T>(0)) == 1;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_container
|
|
: std::integral_constant<bool,
|
|
has_const_iterator<T>::value && has_begin_end<T>::beg_value
|
|
&& has_begin_end<T>::end_value>
|
|
{
|
|
};
|
|
|
|
// Write the content of arrays as value of a CSV field
|
|
// do not use ',' since it is field separator in CSV
|
|
// Example: arrays = [[1, 2], [3, 4]
|
|
// Output: '[[1 2] [3 4]]'
|
|
|
|
// Main print function
|
|
template <typename Container>
|
|
std::ostream& printContainerInCsv(Container const& container, std::ostream& outStream)
|
|
{
|
|
outStream << "[";
|
|
for(int i = 0; i < container.size(); i++)
|
|
{
|
|
if constexpr(is_container<typename Container::value_type>::value)
|
|
{
|
|
printContainerInCsv(container[i], outStream);
|
|
}
|
|
else
|
|
{
|
|
outStream << container[i];
|
|
if(i != container.size() - 1)
|
|
{
|
|
outStream << ' ';
|
|
}
|
|
}
|
|
}
|
|
outStream << "] ";
|
|
return outStream;
|
|
}
|
|
|
|
// Print a std::vector<std::vector<T>>
|
|
template <typename T>
|
|
std::ostream& printVectorInCsv(std::vector<std::vector<T>> const& container,
|
|
std::ostream& outStream)
|
|
{
|
|
outStream << "[";
|
|
for(const auto& vec : container)
|
|
{
|
|
outStream << "[";
|
|
for(const auto& element : vec)
|
|
{
|
|
outStream << element;
|
|
if(&element != &vec.back())
|
|
{
|
|
outStream << " ";
|
|
}
|
|
}
|
|
outStream << "]";
|
|
if(&vec != &container.back())
|
|
{
|
|
outStream << " ";
|
|
}
|
|
}
|
|
outStream << "]";
|
|
return outStream;
|
|
}
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, const hipFloatComplex& fc)
|
|
{
|
|
std::string seperator = (hipCimagf(fc) >= 0) ? " + " : "";
|
|
|
|
return os << hipCrealf(fc) << seperator << hipCimagf(fc) << "i";
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, const hipDoubleComplex& dc)
|
|
{
|
|
std::string seperator = (hipCimag(dc) >= 0) ? " + " : "";
|
|
|
|
return os << hipCreal(dc) << seperator << hipCimag(dc) << "i";
|
|
}
|
|
|
|
template <typename T>
|
|
void hiptensorPrintArrayElements(std::ostream& stream, T* vec, size_t size)
|
|
{
|
|
int index = 0;
|
|
while(index != size)
|
|
{
|
|
if(index == size - 1)
|
|
{
|
|
stream << vec[index];
|
|
}
|
|
else
|
|
{
|
|
stream << vec[index] << ", ";
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
template <typename S>
|
|
void hiptensorPrintVectorElements(const std::vector<S>& vec, std::string sep = " ")
|
|
{
|
|
for(auto& elem : vec)
|
|
{
|
|
std::cout << elem;
|
|
if(&elem != &vec.back())
|
|
{
|
|
std::cout << sep;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename F>
|
|
void hiptensorPrintElementsToFile(std::ofstream& fs, F* output, size_t size, std::string sep = " ")
|
|
{
|
|
if(!fs.is_open())
|
|
{
|
|
std::cout << "File not found!\n";
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
if(i == size - 1)
|
|
{
|
|
fs << static_cast<F>(output[i]);
|
|
}
|
|
else
|
|
{
|
|
fs << static_cast<F>(output[i]) << sep;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
namespace std
|
|
{
|
|
static ostream& operator<<(ostream& os, const hiptensorTensorDescriptor_t desc)
|
|
{
|
|
os << "dim " << desc->mLengths.size() << ", ";
|
|
|
|
os << "lengths {";
|
|
hiptensorPrintVectorElements(desc->mLengths, ", ");
|
|
os << "}, ";
|
|
|
|
os << "strides {";
|
|
hiptensorPrintVectorElements(desc->mStrides, ", ");
|
|
os << "}";
|
|
|
|
return os;
|
|
}
|
|
}
|
|
namespace std
|
|
{
|
|
template <typename T>
|
|
ostream& operator<<(ostream& os, const std::vector<T>& vec)
|
|
{
|
|
os << "[ ";
|
|
for(auto i = 0; i < vec.size(); i++)
|
|
{
|
|
if(i < vec.size() - 1)
|
|
{
|
|
os << vec[i] << ", ";
|
|
}
|
|
else
|
|
{
|
|
os << vec[i];
|
|
}
|
|
}
|
|
os << " ]";
|
|
|
|
return os;
|
|
}
|
|
}
|