Files
TheRock/rocm-libraries/projects/hipblaslt/clients/common/include/testing_matmul.hpp
T
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

6283 lines
279 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*******************************************************************************
*
* MIT License
*
* Copyright (C) 2022-2026 Advanced Micro Devices, Inc.
*
* 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 "TensorDataManipulation.hpp"
#include "allclose.hpp"
#include "benchmark_timing.hpp"
#include "cblas_interface.hpp"
#include "efficiency_monitor.hpp"
#include "flops.hpp"
#include "hipBuffer.hpp"
#include "hipblaslt_bench_options.hpp"
#include "hipblaslt_datatype2string.hpp"
#include "hipblaslt_init.hpp"
#include "hipblaslt_math.hpp"
#include "hipblaslt_random.hpp"
#include "hipblaslt_test.hpp"
#include "hipblaslt_vector.hpp"
#if HIPBLASLT_ENABLE_MXDATAGENERATOR
#include "mxDataGen.hpp"
#endif
#include "near.hpp"
#include "norm.hpp"
#include "ulp.hpp"
#include "unit.hpp"
#include "utility.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <functional>
#include <hipblaslt/hipblaslt-ext-op.h>
#include <hipblaslt/hipblaslt-ext.hpp>
#include <hipblaslt/hipblaslt.h>
#include <iomanip>
#include <map>
#include <numeric>
#include <omp.h>
#include <optional>
#include <set>
extern "C" __global__ void flush_icache()
{
asm __volatile__("s_icache_inv \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t" ::
:);
}
// Convert element count to byte count, accounting for sub-byte packing.
// FP4 (4-bit) packs 2 elements per byte; all other types use realDataTypeSize.
size_t elementsToBytes(size_t numElements, hipDataType dtype)
{
if(static_cast<int>(dtype) == HIP_R_4F_E2M1)
return numElements / 2;
return numElements * realDataTypeSize(dtype);
}
bool isSwizzleSupported(hipDataType datatype)
{
switch(datatype)
{
case HIP_R_16BF:
case HIP_R_16F:
case HIP_R_8F_E4M3_FNUZ:
case HIP_R_4F_E2M1:
return true;
default:
return false;
}
}
bool MXUseRocroller()
{
#ifdef HIPBLASLT_USE_ROCROLLER
return hipblaslt_get_arch() != 1250;
#else
return false;
#endif
}
hipblasLtOrder_t orderForDatatype(hipDataType datatype)
{
switch(datatype)
{
case HIP_R_16F:
case HIP_R_16BF:
return HIPBLASLT_ORDER_COL16_4R8;
case HIP_R_8F_E4M3_FNUZ:
return HIPBLASLT_ORDER_COL16_4R16;
case HIP_R_4F_E2M1:
return HIPBLASLT_ORDER_COL16_4R32;
default:
throw std::runtime_error("unsupported datatype in orderForDatatype");
}
}
void calculateKforSwizzling(
hipDataType datatype, const Arguments& arg, size_t& MiK, size_t& MiKv, size_t& PackK)
{
switch(datatype)
{
case HIP_R_32F:
if(arg.compute_type == HIPBLAS_COMPUTE_32F_FAST_TF32)
{
MiK = 8;
MiKv = 2;
}
else
{
MiK = 4;
MiKv = 1;
}
break;
case HIP_R_64F:
MiK = 4;
MiKv = 1;
break;
case HIP_R_16F:
case HIP_R_16BF:
MiK = 16;
MiKv = 4;
break;
case HIP_R_8I:
case HIP_R_8F_E5M2_FNUZ:
case HIP_R_8F_E4M3_FNUZ:
case HIP_R_8F_E4M3:
case HIP_R_8F_E5M2:
MiK = 32;
MiKv = 8;
break;
case HIP_R_4F_E2M1:
// For fp4 viewed as uint8: matches shuffle_weight with layout=(16,16)
// BK=32 bytes, K=16 bytes, BK/K=2
MiK = 16; // K inner block = 16 bytes
MiKv = 8;
break;
default:
throw std::runtime_error("unsupported datatype in calculateKforSwizzling");
}
PackK = 16 / MiKv / realDataTypeSize(datatype);
}
template <typename T,
std::enable_if_t<true
#if defined(HIPBLASLT_USE_FP6) && defined(HIPBLASLT_USE_BF6)
&& (!std::is_same<hipblaslt_f6x16, T>::value)
&& (!std::is_same<hipblaslt_bf6x16, T>::value)
#endif
#if defined(HIPBLASLT_USE_FP4)
&& (!std::is_same<hipblaslt_f4x2, T>::value)
#endif
,
bool>
= true>
float typeToFloat(T* buf, size_t idx)
{
return static_cast<float>(buf[idx]);
}
#if(defined(HIPBLASLT_USE_FP6) && defined(HIPBLASLT_USE_BF6)) || defined(HIPBLASLT_USE_FP4)
template <typename T,
std::enable_if_t<false
#if defined(HIPBLASLT_USE_FP6) && defined(HIPBLASLT_USE_BF6)
|| std::is_same<hipblaslt_f6x16, T>::value
|| std::is_same<hipblaslt_bf6x16, T>::value
#endif
#if defined(HIPBLASLT_USE_FP4)
|| std::is_same<hipblaslt_f4x2, T>::value
#endif
,
bool>
= true>
float typeToFloat(T* buf, size_t idx)
{
size_t oIdx = idx / T::packed_size;
size_t iIdx = idx % T::packed_size;
return buf[oIdx].castElement(iIdx);
}
#endif
template <typename T, typename S>
std::vector<float> mx_type_to_f32(T* buf, S* sbuf, size_t row, size_t col, size_t srow, size_t scol)
{
std::vector<float> ref(row * col, 0.0f);
for(size_t c = 0; c < col; c++)
{
for(size_t r = 0; r < row; r++)
{
size_t rIndex = r + c * row;
size_t sIndex = r / srow + c / scol * (row / srow);
ref[rIndex] = typeToFloat(buf, rIndex) * fabs(typeToFloat(sbuf, sIndex));
}
}
return ref;
}
// Raw-pointer overload so callers can pass per-batch offsets into packed
// (sub-byte) data buffers via uint8_t* arithmetic without taking a sub-view
// of HipHostBuffer.
std::vector<float> mx_type_to_f32(hipDataType type,
hipDataType stype,
void* buf_ptr,
void* sbuf_ptr,
size_t row,
size_t col,
size_t srow,
size_t scol)
{
switch(type)
{
case HIP_R_8F_E4M3:
switch(stype)
{
case HIP_R_8F_UE8M0:
return mx_type_to_f32(reinterpret_cast<hipblaslt_f8*>(buf_ptr),
reinterpret_cast<hipblaslt_e8*>(sbuf_ptr),
row,
col,
srow,
scol);
default:
hipblaslt_cerr << "Error type in mx_type_to_f32()" << std::endl;
throw std::runtime_error("Error type in mx_type_to_f32()");
}
case HIP_R_8F_E5M2:
switch(stype)
{
case HIP_R_8F_UE8M0:
return mx_type_to_f32(reinterpret_cast<hipblaslt_bf8*>(buf_ptr),
reinterpret_cast<hipblaslt_e8*>(sbuf_ptr),
row,
col,
srow,
scol);
default:
hipblaslt_cerr << "Error type in mx_type_to_f32()" << std::endl;
throw std::runtime_error("Error type in mx_type_to_f32()");
}
#if defined(HIPBLASLT_USE_FP6)
case HIP_R_6F_E2M3:
switch(stype)
{
case HIP_R_8F_UE8M0:
return mx_type_to_f32(reinterpret_cast<hipblaslt_f6x16*>(buf_ptr),
reinterpret_cast<hipblaslt_e8*>(sbuf_ptr),
row,
col,
srow,
scol);
default:
hipblaslt_cerr << "Error type in mx_type_to_f32()" << std::endl;
throw std::runtime_error("Error type in mx_type_to_f32()");
}
#endif
#if defined(HIPBLASLT_USE_BF6)
case HIP_R_6F_E3M2:
switch(stype)
{
case HIP_R_8F_UE8M0:
return mx_type_to_f32(reinterpret_cast<hipblaslt_bf6x16*>(buf_ptr),
reinterpret_cast<hipblaslt_e8*>(sbuf_ptr),
row,
col,
srow,
scol);
default:
hipblaslt_cerr << "Error type in mx_type_to_f32()" << std::endl;
throw std::runtime_error("Error type in mx_type_to_f32()");
}
#endif
#if defined(HIPBLASLT_USE_FP4)
case HIP_R_4F_E2M1:
switch(stype)
{
case HIP_R_8F_UE8M0:
return mx_type_to_f32(reinterpret_cast<hipblaslt_f4x2*>(buf_ptr),
reinterpret_cast<hipblaslt_e8*>(sbuf_ptr),
row,
col,
srow,
scol);
case HIP_R_8F_E4M3:
return mx_type_to_f32(reinterpret_cast<hipblaslt_f4x2*>(buf_ptr),
reinterpret_cast<hipblaslt_f8*>(sbuf_ptr),
row,
col,
srow,
scol);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch"
case HIP_R_8F_E5M3_EXT:
#pragma GCC diagnostic pop
return mx_type_to_f32(reinterpret_cast<hipblaslt_f4x2*>(buf_ptr),
reinterpret_cast<hipblaslt_e5m3*>(sbuf_ptr),
row,
col,
srow,
scol);
default:
hipblaslt_cerr << "Error type in mx_type_to_f32()" << std::endl;
throw std::runtime_error("Error type in mx_type_to_f32()");
}
#endif
default:
hipblaslt_cerr << "Error type in mx_type_to_f32()" << std::endl;
throw std::runtime_error("Error type in mx_type_to_f32()");
}
}
std::vector<float> mx_type_to_f32(hipDataType type,
hipDataType stype,
HipHostBuffer& buf,
HipHostBuffer& sbuf,
size_t row,
size_t col,
size_t srow,
size_t scol)
{
return mx_type_to_f32(type, stype, buf.buf(), sbuf.buf(), row, col, srow, scol);
}
template <typename T>
void swizzle_tensor(T* dst,
const T* src,
hipDataType datatype,
const Arguments& arg,
size_t b,
size_t m_n,
size_t k,
size_t ld,
bool colMaj)
{
if(ld < k)
throw std::runtime_error("invalid value of ld in swizzle_tensor: ld must be >= k.");
using Tensor = Tensor::Manipulation::Tensor;
// currently, if A then it means MiM = 16, if B then it means MiN = 16
size_t MiM_N = 16;
size_t MiK = 0, MiKv = 0, PackK = 0;
calculateKforSwizzling(datatype, arg, MiK, MiKv, PackK);
const size_t numElements = b * m_n * k;
auto tmpTensor = Tensor::create<T>({b, m_n, k});
if(colMaj)
{
auto orgTensor = Tensor::create<T>({b, k, m_n});
for(size_t i = 0; i < b * k; i++)
{
std::copy(src + (i * ld), src + (i * ld) + m_n, orgTensor.template as<T>() + (i * m_n));
}
tmpTensor = permute(orgTensor, {0, 2, 1});
}
else
{
for(size_t i = 0; i < b * m_n; i++)
{
std::copy(src + (i * ld), src + (i * ld) + k, tmpTensor.template as<T>() + (i * k));
}
}
auto MultipleM_N = MiM_N;
auto MultipleK = MiK * PackK;
const auto paddedM_N = (m_n / MultipleM_N + !!(m_n % MultipleM_N)) * MultipleM_N;
const auto paddedK = (k / MultipleK + !!(k % MultipleK)) * MultipleK;
::Tensor::Manipulation::Shape paddedShape{b, paddedM_N, paddedK};
auto paddedTensor = ::Tensor::Manipulation::pad(tmpTensor, paddedShape, T(0));
paddedTensor.reshape(
{b, paddedM_N / MiM_N, MiM_N, paddedK / (MiK * PackK), MiK / MiKv, MiKv * PackK});
Tensor permuted = permute(paddedTensor, {0, 1, 3, 4, 2, 5});
std::copy(
permuted.template as<T>(), permuted.template as<T>() + (b * paddedM_N * paddedK), dst);
}
void swizzle_tensor_type(HipHostBuffer& dst,
const HipHostBuffer& src,
hipDataType datatype,
const Arguments& arg,
size_t b,
size_t m_n,
size_t k,
size_t ld,
bool colMaj)
{
switch(datatype)
{
case HIP_R_32F:
swizzle_tensor<float>(
dst.as<float>(), src.as<float>(), datatype, arg, b, m_n, k, ld, colMaj);
return;
case HIP_R_16F:
swizzle_tensor<hipblasLtHalf>(
dst.as<hipblasLtHalf>(), src.as<hipblasLtHalf>(), datatype, arg, b, m_n, k, ld, colMaj);
return;
case HIP_R_16BF:
swizzle_tensor<hip_bfloat16>(
dst.as<hip_bfloat16>(), src.as<hip_bfloat16>(), datatype, arg, b, m_n, k, ld, colMaj);
return;
case HIP_R_8F_E4M3_FNUZ:
swizzle_tensor<hipblaslt_f8_fnuz>(dst.as<hipblaslt_f8_fnuz>(),
src.as<hipblaslt_f8_fnuz>(),
datatype,
arg,
b,
m_n,
k,
ld,
colMaj);
return;
case HIP_R_8F_E5M2_FNUZ:
swizzle_tensor<hipblaslt_bf8_fnuz>(dst.as<hipblaslt_bf8_fnuz>(),
src.as<hipblaslt_bf8_fnuz>(),
datatype,
arg,
b,
m_n,
k,
ld,
colMaj);
return;
case HIP_R_8F_E4M3:
swizzle_tensor<hipblaslt_f8>(
dst.as<hipblaslt_f8>(), src.as<hipblaslt_f8>(), datatype, arg, b, m_n, k, ld, colMaj);
return;
case HIP_R_8F_E5M2:
swizzle_tensor<hipblaslt_bf8>(
dst.as<hipblaslt_bf8>(), src.as<hipblaslt_bf8>(), datatype, arg, b, m_n, k, ld, colMaj);
return;
case HIP_R_4F_E2M1:
// fp4: 2 elements per byte, so k_bytes = k/2, ld_bytes = ld/2
swizzle_tensor<uint8_t>(
dst.as<uint8_t>(), src.as<uint8_t>(), datatype, arg, b, m_n, k / 2, ld / 2, colMaj);
return;
default:
hipblaslt_cerr << "Error type in swizzle_tensor_type()" << std::endl;
}
}
inline void pre_gpu_time(bool use_gpu_timer,
hipEvent_t& event_gpu_time_start,
double& gpu_time_used,
hipStream_t& stream)
{
if(use_gpu_timer)
CHECK_HIP_ERROR(hipEventRecord(event_gpu_time_start, stream));
else
gpu_time_used = get_time_us_sync(stream);
}
inline void post_gpu_time(bool use_gpu_timer,
hipEvent_t& event_gpu_time_start,
hipEvent_t& event_gpu_time_end,
double& gpu_time_used,
hipStream_t& stream)
{
if(use_gpu_timer)
{
CHECK_HIP_ERROR(hipEventRecord(event_gpu_time_end, stream));
CHECK_HIP_ERROR(hipEventSynchronize(event_gpu_time_end));
float gpu_time_ms;
CHECK_HIP_ERROR(
hipEventElapsedTime(&gpu_time_ms, event_gpu_time_start, event_gpu_time_end));
gpu_time_used = gpu_time_ms * 1000; // ms to us
}
else
{
gpu_time_used = get_time_us_sync(stream) - gpu_time_used;
}
}
template <typename Tout>
Tout cast_from_type(void* in, hipDataType type, size_t index)
{
constexpr bool tout_is_real = !is_std_complex_v<Tout>;
switch(type)
{
case HIP_R_32F:
return static_cast<Tout>((static_cast<float*>(in))[index]);
case HIP_R_64F:
return static_cast<Tout>((static_cast<double*>(in))[index]);
case HIP_C_32F:
{
auto val = (static_cast<std::complex<float>*>(in))[index];
if constexpr(tout_is_real)
return static_cast<Tout>(val.real()); // Extract real part
else
return static_cast<Tout>(val); // Cast complex-to-complex
}
case HIP_C_64F:
{
auto val = (static_cast<std::complex<double>*>(in))[index];
if constexpr(tout_is_real)
return static_cast<Tout>(val.real()); // Extract real part
else
return static_cast<Tout>(val); // Cast complex-to-complex
}
case HIP_R_16F:
return static_cast<Tout>((static_cast<hipblasLtHalf*>(in))[index]);
case HIP_R_16BF:
return static_cast<Tout>((static_cast<hip_bfloat16*>(in))[index]);
case HIP_R_8F_E4M3_FNUZ:
return static_cast<Tout>(
static_cast<hipblasLtHalf>((static_cast<hipblaslt_f8_fnuz*>(in))[index]));
case HIP_R_8F_E5M2_FNUZ:
return static_cast<Tout>(
static_cast<hipblasLtHalf>((static_cast<hipblaslt_bf8_fnuz*>(in))[index]));
case HIP_R_8F_E4M3:
return static_cast<Tout>(
static_cast<hipblasLtHalf>((static_cast<hipblaslt_f8*>(in))[index]));
case HIP_R_8F_E5M2:
return static_cast<Tout>(
static_cast<hipblasLtHalf>((static_cast<hipblaslt_bf8*>(in))[index]));
case HIP_R_32I:
return static_cast<Tout>((static_cast<int32_t*>(in))[index]);
case HIP_R_8I:
return static_cast<Tout>((static_cast<hipblasLtInt8*>(in))[index]);
case HIP_R_6F_E2M3:
hipblaslt_cerr << "cast_from_type() does not support FP6" << std::endl;
return 0;
case HIP_R_6F_E3M2:
hipblaslt_cerr << "cast_from_type() does not support BF6" << std::endl;
return 0;
case HIP_R_4F_E2M1:
hipblaslt_cerr << "cast_from_type() does not support FP4" << std::endl;
return 0;
default:
hipblaslt_cerr << "Error type in cast_from_type()" << std::endl;
return 0;
}
}
template <typename Tin>
void saturate_cast_to_type(void* dst, Tin src, hipDataType typeD, size_t indexD)
{
switch(typeD)
{
case HIP_R_32F:
static_cast<float*>(dst)[indexD] = saturate_cast<float>(src);
return;
case HIP_R_64F:
static_cast<double*>(dst)[indexD] = saturate_cast<double>(src);
return;
case HIP_R_16F:
static_cast<hipblasLtHalf*>(dst)[indexD] = saturate_cast<hipblasLtHalf>(src);
return;
case HIP_R_16BF:
static_cast<hip_bfloat16*>(dst)[indexD] = saturate_cast<hip_bfloat16>(src);
return;
case HIP_R_8F_E4M3_FNUZ:
static_cast<hipblaslt_f8_fnuz*>(dst)[indexD] = saturate_cast<hipblaslt_f8_fnuz>(src);
return;
case HIP_R_8F_E5M2_FNUZ:
static_cast<hipblaslt_bf8_fnuz*>(dst)[indexD] = saturate_cast<hipblaslt_bf8_fnuz>(src);
return;
case HIP_R_8F_E4M3:
static_cast<hipblaslt_f8*>(dst)[indexD] = saturate_cast<hipblaslt_f8>(src);
return;
case HIP_R_8F_E5M2:
static_cast<hipblaslt_bf8*>(dst)[indexD] = saturate_cast<hipblaslt_bf8>(src);
return;
case HIP_R_32I:
static_cast<int32_t*>(dst)[indexD] = saturate_cast<int32_t>(src);
return;
case HIP_R_8I:
static_cast<hipblasLtInt8*>(dst)[indexD] = saturate_cast<hipblasLtInt8>(src);
return;
case HIP_R_6F_E2M3:
hipblaslt_cerr << "cast_from_type() does not support FP6!" << std::endl;
return;
case HIP_R_6F_E3M2:
hipblaslt_cerr << "cast_from_type() does not support BF6!" << std::endl;
return;
case HIP_R_4F_E2M1:
hipblaslt_cerr << "cast_from_type() does not support FP4!" << std::endl;
return;
default:
hipblaslt_cerr << "Error type in cast_from_type()" << std::endl;
}
}
template <typename Ti, typename Tc, typename Tact, typename F>
void epilogue_func(int64_t m,
int64_t n,
int64_t ld,
Ti* in,
void* out,
Tc* out_raw,
Tc* amaxD,
void* e,
hipDataType aux_type,
Tc scaleD,
Tc scaleE,
bool enable_bias,
void* bias,
hipDataType bias_type,
Tact arg1,
Tact arg2,
F& act_func,
bool gradient,
hipDataType To)
{
for(int i = 0; i < m; i++)
{
Ti bias_data = enable_bias ? cast_from_type<Ti>(bias, bias_type, i) : 0;
#define CALCULATE_EPILOGUE_ACT \
auto pos = j * ld + i; \
auto in_Tact = static_cast<Tact>(in[pos]) + bias_data; \
if(e && !gradient) \
{ \
saturate_cast_to_type(e, in_Tact * scaleE, aux_type, pos); \
} \
Tact in_Tact_act = 0; \
if(gradient) \
{ \
in_Tact_act = act_func(cast_from_type<Tact>(e, aux_type, pos), arg1, arg2) * in_Tact; \
} \
else \
in_Tact_act = act_func(in_Tact, arg1, arg2);
if(amaxD == nullptr)
{
#pragma omp parallel for
for(int j = 0; j < n; j++)
{
CALCULATE_EPILOGUE_ACT;
saturate_cast_to_type(out, in_Tact_act * scaleD, To, pos);
*(out_raw + pos) = static_cast<Tc>(in_Tact_act * scaleD);
}
}
else
{
for(int j = 0; j < n; j++)
{
CALCULATE_EPILOGUE_ACT;
*amaxD = *amaxD > std::abs(static_cast<Tc>(in_Tact_act))
? *amaxD
: std::abs(static_cast<Tc>(in_Tact_act));
saturate_cast_to_type(out, in_Tact_act * scaleD, To, pos);
*(out_raw + pos) = static_cast<Tc>(in_Tact_act * scaleD);
}
}
}
}
template <typename Tact, typename F>
void epilogue_func(int64_t m,
int64_t n,
int64_t ld,
void* in,
void* out,
void* out_raw,
void* amaxD,
void* e,
hipDataType aux_type,
void* scaleD,
void* scaleE,
bool enable_bias,
void* bias,
hipDataType bias_type,
Tact arg1,
Tact arg2,
F& act_func,
bool gradient,
hipDataType To,
hipDataType Tc)
{
switch(Tc)
{
case HIP_R_32F:
epilogue_func(m,
n,
ld,
(float*)in,
out,
(float*)out_raw,
(float*)amaxD,
e,
aux_type,
*(float*)scaleD,
*(float*)scaleE,
enable_bias,
bias,
bias_type,
arg1,
arg2,
act_func,
gradient,
To);
return;
case HIP_R_64F:
epilogue_func(m,
n,
ld,
(double*)in,
out,
(double*)out_raw,
(double*)amaxD,
e,
aux_type,
*(double*)scaleD,
*(double*)scaleE,
enable_bias,
bias,
bias_type,
arg1,
arg2,
act_func,
gradient,
To);
return;
case HIP_R_32I:
epilogue_func(m,
n,
ld,
(int32_t*)in,
out,
(int32_t*)out_raw,
(int32_t*)amaxD,
e,
aux_type,
*(int32_t*)scaleD,
*(int32_t*)scaleE,
enable_bias,
bias,
bias_type,
arg1,
arg2,
act_func,
gradient,
To);
return;
default:
hipblaslt_cerr << "Error type in epilogue_func()" << std::endl;
return;
}
}
template <typename Ti, typename Tc>
void epilogue_func(int64_t m,
int64_t n,
int64_t ld,
Ti* in,
void* out,
Tc* out_raw,
Tc* amaxD,
void* e,
hipDataType aux_type,
Tc scaleD,
Tc scaleE,
bool enable_bias,
void* bias,
hipDataType bias_type,
bool gradient,
hipDataType To)
{
#define CALCULATE_EPILOGUE_BASIC \
auto pos = j * ld + i; \
Tc temp = static_cast<Ti>(*(in + pos)) + bias_data; \
if(e) \
{ \
saturate_cast_to_type(e, temp * scaleE, aux_type, pos); \
}
for(int i = 0; i < m; i++)
{
Ti bias_data = enable_bias ? cast_from_type<Ti>(bias, bias_type, i) : 0;
if(amaxD == nullptr)
{
#pragma omp parallel for
for(int j = 0; j < n; j++)
{
CALCULATE_EPILOGUE_BASIC;
temp *= scaleD;
saturate_cast_to_type(out, temp, To, pos);
*(out_raw + pos) = static_cast<Tc>(temp);
}
}
else
{
for(int j = 0; j < n; j++)
{
CALCULATE_EPILOGUE_BASIC;
*amaxD = *amaxD > std::abs(static_cast<Tc>(temp)) ? *amaxD
: std::abs(static_cast<Tc>(temp));
temp *= scaleD;
saturate_cast_to_type(out, temp, To, pos);
*(out_raw + pos) = static_cast<Tc>(temp);
}
}
}
}
void epilogue_func(int64_t m,
int64_t n,
int64_t ld,
void* in,
void* out,
void* out_raw,
void* amaxD,
void* e,
hipDataType aux_type,
void* scaleD,
void* scaleE,
bool enable_bias,
void* bias,
hipDataType bias_type,
bool gradient,
hipDataType To,
hipDataType Tc)
{
switch(Tc)
{
case HIP_R_32F:
epilogue_func(m,
n,
ld,
(float*)in,
out,
(float*)out_raw,
(float*)amaxD,
e,
aux_type,
*(float*)scaleD,
*(float*)scaleE,
enable_bias,
bias,
bias_type,
gradient,
To);
return;
case HIP_R_64F:
epilogue_func(m,
n,
ld,
(double*)in,
out,
(double*)out_raw,
(double*)amaxD,
e,
aux_type,
*(double*)scaleD,
*(double*)scaleE,
enable_bias,
bias,
bias_type,
gradient,
To);
return;
case HIP_R_32I:
epilogue_func(m,
n,
ld,
(int32_t*)in,
out,
(int32_t*)out_raw,
(int32_t*)amaxD,
e,
aux_type,
*(int32_t*)scaleD,
*(int32_t*)scaleE,
enable_bias,
bias,
bias_type,
gradient,
To);
return;
default:
hipblaslt_cerr << "Error type in epilogue_func()" << std::endl;
return;
}
}
template <bool SumLd, typename Tc>
void reduction_func(void* workspace,
hipDataType ti,
void* bias,
hipDataType bias_type,
int length,
int k,
int s1,
int s2,
int s3,
int batch_count)
{
assert(batch_count == 1);
for(int batch = 0; batch < batch_count; batch++)
{
for(int i1 = 0; i1 < length; i1++)
{
Tc sum = 0;
for(int i2 = 0; i2 < k; i2++)
{
if constexpr(SumLd)
{
sum += cast_from_type<Tc>(workspace, ti, i1 * s2 + i2 * s1 + batch * s3);
}
else
{
sum += cast_from_type<Tc>(workspace, ti, i1 * s1 + i2 * s2 + batch * s3);
}
}
saturate_cast_to_type(bias, sum, bias_type, i1);
}
}
}
auto _relu = [](auto in, auto /*arg1*/, auto /*arg2*/) -> decltype(in) {
return static_cast<decltype(in)>(std::max(static_cast<decltype(in)>(0), in));
};
auto _drelu = [](auto in, auto /*arg1*/, auto /*arg2*/) -> decltype(in) {
return static_cast<decltype(in)>(in > static_cast<decltype(in)>(0) ? 1 : 0);
};
auto _gelu = [](auto in, auto /*arg1*/, auto /*arg2*/) -> decltype(in) {
using Tc = float;
constexpr auto k0 = static_cast<Tc>(0.7978845608028654);
constexpr auto k1 = static_cast<Tc>(0.044715);
Tc in_Tc = static_cast<Tc>(in);
return static_cast<decltype(in)>(
0.5f * (in_Tc * (1.f + std::tanh(k0 * (in_Tc * (1.f + k1 * (in_Tc * in_Tc)))))));
};
auto _dgelu = [](auto in, auto /*arg1*/, auto /*arg2*/) -> decltype(in) {
using Tc = float;
constexpr auto k0 = static_cast<Tc>(0.0535161);
constexpr auto k1 = static_cast<Tc>(0.398942);
constexpr auto k2 = static_cast<Tc>(0.0356774);
constexpr auto k3 = static_cast<Tc>(0.797885);
Tc in_Tc = static_cast<Tc>(in);
Tc pow3 = in_Tc * in_Tc * in_Tc;
Tc x1 = k0 * pow3 + k1 * in_Tc;
Tc xx = k2 * pow3 + k3 * in_Tc;
Tc x2 = 4 / pow(exp(-xx) + exp(xx), 2);
Tc tmp = 0.5 * tanh(xx) + x1 * x2 + 0.5;
return static_cast<decltype(in)>(0.5f * tanh(xx) + x1 * x2 + 0.5f);
};
// swish with beta=1
auto _silu = [](auto in, auto /*arg1*/, auto /*arg2*/) -> decltype(in) {
using Tc = float;
Tc in_Tc = static_cast<Tc>(in);
return static_cast<decltype(in)>(in_Tc / (1.f + exp(-in_Tc)));
};
// clamp
auto _clamp = [](auto in, auto alpha, auto beta) -> decltype(in) {
using Tc = float;
Tc in_Tc = static_cast<Tc>(in);
return static_cast<decltype(in)>(
std::max(static_cast<Tc>(alpha), std::min(in_Tc, static_cast<Tc>(beta))));
};
auto _sigmoid = [](auto in, auto /*arg1*/, auto /*arg2*/) -> decltype(in) {
return static_cast<decltype(in)>(static_cast<decltype(in)>(1)
/ (static_cast<decltype(in)>(1) + std::exp(-in)));
};
void testing_matmul_bad_arg(const Arguments& arg)
{
const int64_t M = 128;
const int64_t N = 128;
const int64_t K = 128;
const int64_t lda = 128;
const int64_t ldb = 128;
const int64_t ldc = 128;
const size_t safe_size = N * lda;
const hipblasOperation_t transA = HIPBLAS_OP_T;
const hipblasOperation_t transB = HIPBLAS_OP_N;
// allocate memory on device
HipDeviceBuffer dA(arg.a_type, safe_size / 2, arg.HMM);
HipDeviceBuffer dB(arg.b_type, safe_size, arg.HMM);
HipDeviceBuffer dC(arg.c_type, safe_size, arg.HMM);
HipDeviceBuffer dD(arg.d_type, safe_size, arg.HMM);
CHECK_DEVICE_ALLOCATION(dA.memcheck());
CHECK_DEVICE_ALLOCATION(dB.memcheck());
CHECK_DEVICE_ALLOCATION(dC.memcheck());
CHECK_DEVICE_ALLOCATION(dD.memcheck());
hipblaslt_local_handle handle{arg};
hipblaslt_local_matrix_layout matA(M, K, lda, arg.a_type);
hipblaslt_local_matrix_layout matB(K, N, ldb, arg.b_type);
hipblaslt_local_matrix_layout matC(M, N, ldc, arg.c_type);
hipblaslt_local_matrix_layout matD(M, N, ldc, arg.d_type);
hipblaslt_local_matmul_descr matmul(transA,
transB,
arg.compute_type,
arg.scale_type,
arg.compute_input_typeA,
arg.compute_input_typeB);
size_t workspace_size = 0;
hipblaslt_local_preference pref;
void* workspace = nullptr;
float alpha = 1.0, beta = 0.0;
hipStream_t stream = nullptr;
}
void copy_gemm_to_host(hipStream_t stream,
const uint32_t& gemm_count,
std::vector<HipHostBuffer>& hDst,
std::vector<HipDeviceBuffer>& dSrc)
{
CHECK_HIP_ERROR(hipStreamSynchronize(stream));
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
CHECK_HIP_ERROR(synchronize(hDst[gemmIdx], dSrc[gemmIdx], 0, 0, 0, 0, 1, false, stream));
}
}
template <typename T>
void dumpBuffer(const char* title, T* buf, size_t M, size_t N)
{
hipblaslt_cout << "----- DUMP: " << title << " -----" << std::endl;
for(int n = 0; n < N; n++)
{
for(int m = 0; m < M; m++)
{
hipblaslt_cout << buf[m + n * M] << " ";
}
hipblaslt_cout << std::endl;
}
}
void dumpBuffer(const char* title, hipDataType To, HipHostBuffer& buf, size_t M, size_t N)
{
switch(To)
{
case HIP_R_32F:
dumpBuffer(title, buf.as<float>(), M, N);
break;
default:
hipblaslt_cerr << "Error type in near_check_general" << std::endl;
break;
}
return;
}
void check(hipStream_t stream,
const Arguments& arg,
const uint32_t& gemm_count,
const std::vector<int64_t>& M,
const std::vector<int64_t>& N,
const std::vector<int64_t>& ldd,
const std::vector<int64_t>& lde,
const std::vector<int64_t>& stride_d,
const std::vector<int64_t>& stride_e,
const std::vector<int>& num_batches,
const std::vector<size_t>& size_bias,
std::vector<HipHostBuffer>& hD_gold,
std::vector<HipHostBuffer>& hD_1,
std::vector<HipDeviceBuffer>& dD,
std::vector<HipHostBuffer>& hAmaxD_gold,
std::vector<HipHostBuffer>& hAmaxD,
std::vector<HipDeviceBuffer>& dAmaxD,
std::vector<HipHostBuffer>& hE_gold,
std::vector<HipHostBuffer>& hE,
std::vector<HipDeviceBuffer>& dE,
std::vector<HipHostBuffer>& hBias_gold,
std::vector<HipHostBuffer>& hBias,
std::vector<HipDeviceBuffer>& dBias,
std::vector<double>& tol,
double& hipblaslt_error,
double& hipblaslt_atol,
double& hipblaslt_rtol,
double& hipblaslt_max_ulp,
double& hipblaslt_avg_ulp,
hipDataType To,
hipDataType Tbias,
hipDataType Taux,
hipDataType Tc,
hipblasLtBatchMode_t batchMode = HIPBLASLT_BATCH_MODE_STRIDED)
{
// fetch GPU
CHECK_HIP_ERROR(hipStreamSynchronize(stream));
// ULP error accumulators (sum/count are used to derive the average below)
double ulp_sum_total = 0.0;
size_t ulp_count_total = 0;
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
if(!arg.gradient && arg.use_e)
{
CHECK_HIP_ERROR(
synchronize(hE[gemmIdx], dE[gemmIdx], 0, 0, 0, 0, 1, false, stream));
}
if(arg.amaxD)
{
CHECK_HIP_ERROR(
synchronize(hAmaxD[gemmIdx], dAmaxD[gemmIdx], 0, 0, 0, 0, 1, false, stream));
}
if(arg.gradient && arg.bias_vector)
{
CHECK_HIP_ERROR(
synchronize(hBias[gemmIdx], dBias[gemmIdx], 0, 0, 0, 0, 1, false, stream));
}
}
if(arg.unit_check)
{
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
if(tol[gemmIdx] != 0)
{
near_check_general(M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
stride_d[gemmIdx],
hD_gold[gemmIdx].buf(),
hD_1[gemmIdx].buf(),
num_batches[gemmIdx],
tol[gemmIdx],
To);
}
else
{
unit_check_general(M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
stride_d[gemmIdx],
hD_gold[gemmIdx].buf(),
hD_1[gemmIdx].buf(),
num_batches[gemmIdx],
To);
}
}
else if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
for(int batch = 0; batch < num_batches[gemmIdx]; batch++)
{
if(tol[gemmIdx] != 0)
{
near_check_general(M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
0,
hD_gold[batch].buf(),
hD_1[batch].buf(),
1,
tol[gemmIdx],
To);
}
else
{
unit_check_general(M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
0,
hD_gold[batch].buf(),
hD_1[batch].buf(),
1,
To);
}
}
}
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
if(arg.amaxD)
{
if(tol[gemmIdx] != 0)
{
near_check_general(1,
1,
1,
1,
hAmaxD_gold[gemmIdx].buf(),
hAmaxD[gemmIdx].buf(),
num_batches[gemmIdx],
tol[gemmIdx],
Tc);
}
else
{
unit_check_general(1,
1,
1,
1,
hAmaxD_gold[gemmIdx].buf(),
hAmaxD[gemmIdx].buf(),
num_batches[gemmIdx],
Tc);
}
}
if(!arg.gradient && arg.use_e)
{
if(tol[gemmIdx] != 0)
{
near_check_general(M[gemmIdx],
N[gemmIdx],
lde[gemmIdx],
stride_e[gemmIdx],
hE_gold[gemmIdx].buf(),
hE[gemmIdx].buf(),
num_batches[gemmIdx],
tol[gemmIdx],
Taux);
}
else
{
unit_check_general(M[gemmIdx],
N[gemmIdx],
lde[gemmIdx],
stride_e[gemmIdx],
hE_gold[gemmIdx].buf(),
hE[gemmIdx].buf(),
num_batches[gemmIdx],
Taux);
}
}
if(arg.gradient && arg.bias_vector)
{
if(tol[gemmIdx] != 0)
{
near_check_general(size_bias[gemmIdx],
1,
size_bias[gemmIdx],
size_bias[gemmIdx],
hBias_gold[gemmIdx].buf(),
hBias[gemmIdx].buf(),
num_batches[gemmIdx],
tol[gemmIdx],
Tbias);
}
else
{
unit_check_general(size_bias[gemmIdx],
1,
size_bias[gemmIdx],
size_bias[gemmIdx],
hBias_gold[gemmIdx].buf(),
hBias[gemmIdx].buf(),
num_batches[gemmIdx],
Tbias);
}
}
}
}
if(arg.norm_check)
{
double norm_error = 0.0;
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
norm_error = std::abs(norm_check_general('F',
M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
stride_d[gemmIdx],
hD_gold[gemmIdx].buf(),
hD_1[gemmIdx].buf(),
num_batches[gemmIdx],
To));
}
else
{
for(int batch = 0; batch < num_batches[gemmIdx]; batch++)
{
norm_error = std::abs(norm_check_general('F',
M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
0,
hD_gold[batch].buf(),
hD_1[batch].buf(),
1,
To));
hipblaslt_error += norm_error;
}
}
hipblaslt_error += norm_error;
if(arg.norm_check_assert)
{
CHECK_SUCCESS(
norm_check(norm_error, To, arg.compute_type, arg.a_type, arg.b_type));
}
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
if(arg.amaxD)
{
double norm_error = std::abs(norm_check_general('F',
1,
1,
1,
1,
hAmaxD_gold[gemmIdx].buf(),
hAmaxD[gemmIdx].buf(),
num_batches[gemmIdx],
Tc));
hipblaslt_error += norm_error;
if(arg.norm_check_assert)
CHECK_SUCCESS(norm_check(norm_error, Tc));
}
if(!arg.gradient && arg.use_e)
{
double norm_error = 0.0;
norm_error = std::abs(norm_check_general('F',
M[gemmIdx],
N[gemmIdx],
lde[gemmIdx],
stride_e[gemmIdx],
hE_gold[gemmIdx].buf(),
hE[gemmIdx].buf(),
num_batches[gemmIdx],
Taux));
hipblaslt_error += norm_error;
if(arg.norm_check_assert)
{
CHECK_SUCCESS(
norm_check(norm_error, Taux, arg.compute_type, arg.a_type, arg.b_type));
}
}
if(arg.gradient && arg.bias_vector)
{
double norm_error = 0.0;
norm_error = std::abs(norm_check_general('F',
M[gemmIdx],
1,
M[gemmIdx],
M[gemmIdx],
hBias_gold[gemmIdx].buf(),
hBias[gemmIdx].buf(),
num_batches[gemmIdx],
Tbias));
hipblaslt_error += norm_error;
if(arg.norm_check_assert)
{
CHECK_SUCCESS(norm_check(norm_error, Tbias));
}
}
}
}
if(arg.allclose_check)
{
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
bool is_allclose = allclose_check_general('F',
M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
stride_d[gemmIdx],
hD_gold[gemmIdx].buf(),
hD_1[gemmIdx].buf(),
num_batches[gemmIdx],
hipblaslt_atol,
hipblaslt_rtol,
To);
}
else
{
for(int batch = 0; batch < num_batches[gemmIdx]; batch++)
{
bool is_allclose = allclose_check_general('F',
M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
0,
hD_gold[batch].buf(),
hD_1[batch].buf(),
1,
hipblaslt_atol,
hipblaslt_rtol,
To);
}
}
//TODO: confirm if allclose_check_assert is neccessary
}
if(arg.ulp_check)
{
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
ulp_check_general(M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
stride_d[gemmIdx],
hD_gold[gemmIdx].buf(),
hD_1[gemmIdx].buf(),
num_batches[gemmIdx],
hipblaslt_max_ulp,
ulp_sum_total,
ulp_count_total,
To);
}
else
{
for(int batch = 0; batch < num_batches[gemmIdx]; batch++)
{
ulp_check_general(M[gemmIdx],
N[gemmIdx],
ldd[gemmIdx],
0,
hD_gold[batch].buf(),
hD_1[batch].buf(),
1,
hipblaslt_max_ulp,
ulp_sum_total,
ulp_count_total,
To);
}
}
}
}
if(arg.ulp_check && ulp_count_total > 0)
hipblaslt_avg_ulp = ulp_sum_total / ulp_count_total;
}
// A function to determine the default bias_type
hipDataType derive_unset_bias_type(const Arguments& arg)
{
// TODO: confirm if HIP_R_64F, HIP_R_32I are neccessary for biastype
static const std::set<hipDataType> supported_bias_types
= {HIP_R_32F, HIP_R_16F, HIP_R_16BF, HIP_R_64F, HIP_R_32I, HIP_C_32F, HIP_C_64F};
hipDataType real_bias_type = arg.bias_type;
// when bias type is unset
if(arg.bias_type == HIPBLASLT_DATATYPE_INVALID)
{
if(arg.compute_type == HIPBLAS_COMPUTE_32I)
{
real_bias_type = HIP_R_32I;
}
else if(arg.compute_type == HIPBLAS_COMPUTE_32F_FAST_TF32)
{
real_bias_type = HIP_R_32F;
}
else if((arg.a_type == HIP_R_8F_E4M3_FNUZ || arg.a_type == HIP_R_8F_E5M2_FNUZ)
&& (arg.b_type == HIP_R_8F_E4M3_FNUZ || arg.b_type == HIP_R_8F_E5M2_FNUZ))
{
if(arg.d_type == HIP_R_32F || arg.d_type == HIP_R_16BF)
real_bias_type = HIP_R_16BF;
else if(arg.d_type == HIP_R_16F)
real_bias_type = HIP_R_16F;
else //more default cases once support C != D
real_bias_type = HIP_R_16F;
}
else if((arg.a_type == HIP_R_8F_E4M3 || arg.a_type == HIP_R_8F_E5M2)
&& (arg.b_type == HIP_R_8F_E4M3 || arg.b_type == HIP_R_8F_E5M2))
{
if(arg.d_type == HIP_R_32F || arg.d_type == HIP_R_16BF)
real_bias_type = HIP_R_16BF;
else if(arg.d_type == HIP_R_16F)
real_bias_type = HIP_R_16F;
else //more default cases once support C != D
real_bias_type = HIP_R_16F;
}
else if((arg.a_type == HIP_R_6F_E2M3 && arg.b_type == HIP_R_6F_E2M3)
|| (arg.a_type == HIP_R_6F_E3M2 && arg.b_type == HIP_R_6F_E3M2)
|| (arg.a_type == HIP_R_4F_E2M1 && arg.b_type == HIP_R_4F_E2M1))
{
if(arg.d_type == HIP_R_32F || arg.d_type == HIP_R_16BF)
real_bias_type = HIP_R_16BF;
else if(arg.d_type == HIP_R_16F)
real_bias_type = HIP_R_16F;
else
real_bias_type = HIP_R_16F;
}
else
{
real_bias_type = arg.d_type;
}
}
if(supported_bias_types.count(real_bias_type) == 0)
throw std::invalid_argument("Invalid bias type "
+ std::string(hip_datatype_to_string(real_bias_type)));
return real_bias_type;
}
// A function to determine the default aux_type
hipDataType derive_unset_aux_type(const Arguments& arg)
{
static const std::set<hipDataType> supported_aux_types = {
HIP_R_16F,
HIP_R_16BF,
HIP_R_8F_E4M3_FNUZ,
HIP_R_8F_E4M3,
};
hipDataType real_aux_type = arg.aux_type;
// when aux type is unset
if(arg.aux_type == HIPBLASLT_DATATYPE_INVALID)
{
real_aux_type = arg.d_type;
}
if(real_aux_type != arg.d_type && supported_aux_types.count(real_aux_type) == 0)
throw std::invalid_argument("Invalid aux type "
+ std::string(hip_datatype_to_string(real_aux_type)));
return real_aux_type;
}
// A function to determine the default compute_input_type
std::tuple<hipDataType, hipDataType> derive_unset_compute_input_type(const Arguments& arg)
{
static const std::set<hipDataType> supported_compute_input_types = {
HIP_R_32F,
HIP_R_16BF,
HIP_R_16F,
HIP_R_8F_E4M3,
HIP_R_8F_E5M2,
HIP_R_8F_E4M3_FNUZ,
HIP_R_8F_E5M2_FNUZ,
static_cast<hipDataType>(HIP_R_6F_E2M3),
static_cast<hipDataType>(HIP_R_6F_E3M2),
static_cast<hipDataType>(HIP_R_4F_E2M1),
};
hipDataType real_compute_input_typeA = arg.compute_input_typeA;
hipDataType real_compute_input_typeB = arg.compute_input_typeB;
if(real_compute_input_typeA != HIPBLASLT_DATATYPE_INVALID
&& !supported_compute_input_types.count(real_compute_input_typeA))
throw std::invalid_argument(
"Invalid compute_input_typeA "
+ std::string(hip_datatype_to_string(real_compute_input_typeA)));
if(real_compute_input_typeA != HIPBLASLT_DATATYPE_INVALID
&& !supported_compute_input_types.count(real_compute_input_typeB))
throw std::invalid_argument(
"Invalid compute_input_typeB "
+ std::string(hip_datatype_to_string(real_compute_input_typeB)));
// when compute_input_type type is unset
if(real_compute_input_typeA == HIPBLASLT_DATATYPE_INVALID)
{
real_compute_input_typeA = computeTypeToRealDataType(arg.compute_type);
}
if(real_compute_input_typeB == HIPBLASLT_DATATYPE_INVALID)
{
real_compute_input_typeB = computeTypeToRealDataType(arg.compute_type);
}
return {real_compute_input_typeA, real_compute_input_typeB};
}
// Swizzle MX scale tensor for the new MX layout expected by the kernel.
// The kernel expects scale data in a permuted layout where the K-block dimension
// is split into outer tiles of size dimk (=128/MXBlock) and interleaved with the
// tiled (M or N) dimension.
//
// scaleRows/scaleCols: dimensions of the scale matrix in column-major storage
// MXBlock: the MX block size (e.g. 16)
// kAlongRows: true if the K-block dimension is along rows of the scale matrix
// transA=T scaleA: scale is (K/MX) x M -> kAlongRows = true
// transA=N scaleA: scale is M x (K/MX) -> kAlongRows = false
// transB=N scaleB: scale is (K/MX) x N -> kAlongRows = true
// transB=T scaleB: scale is N x (K/MX) -> kAlongRows = false
//
// Returns the total number of elements in the swizzled (potentially padded) buffer.
// Raw-pointer overload so callers can pass per-batch offsets into the scale
// buffer via uint8_t* arithmetic. hScaleA/B hold num_batches scale blocks
// concatenated (size_scale*Vec[i] bytes each), and each block must be swizzled
// independently.
size_t swizzle_mx_scale(void* scalePtr,
size_t scaleRows,
size_t scaleCols,
size_t MXBlock,
bool kAlongRows)
{
using Tensor = Tensor::Manipulation::Tensor;
size_t dimk = 128 / MXBlock;
// hipblaslt-bench stores scale in column-major: (scaleRows x scaleCols) with
// scaleRows as the fastest varying dimension (stride 1).
// In row-major Tensor convention: Tensor({scaleCols, scaleRows}) has scaleCols
// as slow dim and scaleRows as fast dim, matching the column-major layout.
//
// The tensile-client MXSA descriptor for Alik (transA=T) has sizes [batch, M, K/MX]
// with M varying fastest (stride 1). Its tmpTensor({K/MX, M}) has M as fast dim,
// matching tensile's memory layout.
//
// hipblaslt-bench transA=T: scale is (K/MX rows x M cols), col-major:
// K/MX is fastest. As row-major Tensor({M, K/MX}): K/MX is fastest.
// To match tensile-client's Tensor({K/MX, M}) where M is fastest,
// we must interpret it as Tensor({scaleCols, scaleRows}) = Tensor({M, K/MX}).
// But tensile wants Tensor({K/MX, M}), which is a different memory order.
// Since hipblaslt has K/MX fastest and tensile has M fastest, the memory layouts
// differ by a transpose. We handle this by using Tensor({scaleCols, scaleRows})
// which matches hipblaslt's actual memory layout, and adapting the permutation.
if(kAlongRows)
{
// K-blocks along rows: scaleRows = K/MX, scaleCols = M (or N)
// hipblaslt col-major memory: K/MX fastest → row-major Tensor({M, K/MX})
// Tensile: Tensor({K/MX, M}) with M fastest (different layout)
//
// We work with hipblaslt's native layout: Tensor({scaleCols, scaleRows}) = Tensor({M, K/MX})
// The swizzle needs to group K/MX (the fast/last dim) into dimk-sized blocks.
auto mnDim = scaleCols; // M
auto kDim = scaleRows; // K/MX
auto tmpTensor = Tensor({mnDim, kDim}, sizeof(uint8_t));
memcpy(tmpTensor.as<void>(), scalePtr, mnDim * kDim);
// Pad kDim (K/MX, the fast dim) to multiple of dimk
::Tensor::Manipulation::Shape paddedShape{mnDim, (kDim + dimk - 1) / dimk * dimk};
uint64_t padVal{};
auto paddedTensor
= ::Tensor::Manipulation::pad(tmpTensor, paddedShape, &padVal, sizeof(uint8_t));
// Reshape: {M, padK/dimk, dimk}
paddedTensor.reshape({paddedShape[0], paddedShape[1] / dimk, dimk});
// Permute {1,0,2}: {padK/dimk, M, dimk}
Tensor permuted = permute(paddedTensor, {1, 0, 2});
auto totalElements = permuted.getDesc().flattenSize();
memcpy(scalePtr, permuted.as<void>(), totalElements);
return totalElements;
}
else
{
// K-blocks along cols: scaleRows = M (or N), scaleCols = K/MX
// hipblaslt col-major memory: M fastest → row-major Tensor({K/MX, M})
// This actually matches tensile's layout: Tensor({K/MX, M}) with M fastest
auto kDim = scaleCols; // K/MX
auto mnDim = scaleRows; // M
auto tmpTensor = Tensor({kDim, mnDim}, sizeof(uint8_t));
memcpy(tmpTensor.as<void>(), scalePtr, kDim * mnDim);
// Pad mnDim (M, the fast dim) to multiple of dimk
::Tensor::Manipulation::Shape paddedShape{kDim, (mnDim + dimk - 1) / dimk * dimk};
uint64_t padVal{};
auto paddedTensor
= ::Tensor::Manipulation::pad(tmpTensor, paddedShape, &padVal, sizeof(uint8_t));
// Reshape: {K/MX, padM/dimk, dimk}
paddedTensor.reshape({paddedShape[0], paddedShape[1] / dimk, dimk});
// Permute {1,0,2}: {padM/dimk, K/MX, dimk}
Tensor permuted = permute(paddedTensor, {1, 0, 2});
auto totalElements = permuted.getDesc().flattenSize();
memcpy(scalePtr, permuted.as<void>(), totalElements);
return totalElements;
}
}
// Convenience overload: swizzle a single scale block held by a HipHostBuffer.
size_t swizzle_mx_scale(HipHostBuffer& scaleBuf,
size_t scaleRows,
size_t scaleCols,
size_t MXBlock,
bool kAlongRows)
{
return swizzle_mx_scale(scaleBuf.buf(), scaleRows, scaleCols, MXBlock, kAlongRows);
}
void testing_matmul_with_bias(const Arguments& arg,
hipDataType TiA,
hipDataType TiB,
hipDataType To,
hipDataType Tc,
hipDataType TciA,
hipDataType TciB,
hipDataType Tbias,
hipDataType Taux);
void testing_matmul(const Arguments& arg)
{
hipDataType tiA = arg.a_type;
hipDataType tiB = arg.b_type;
hipDataType to = arg.c_type;
hipDataType tc = computeTypeToRealDataType(arg.compute_type);
hipDataType tciA, tciB;
// after this, tciA and tciB should not be invalid
std::tie(tciA, tciB) = derive_unset_compute_input_type(arg);
// after this, real bias type should not be invalid
hipDataType real_bias_type = derive_unset_bias_type(arg);
Arguments arg_revised = arg;
arg_revised.bias_type = real_bias_type;
hipDataType real_aux_type = derive_unset_aux_type(arg);
arg_revised.aux_type = real_aux_type;
// Set the values of flush, rotating size, cold_iters and hot_iters only for internal use
hipblasltSetFlushValue(arg.flush);
hipblasltSetRotatingBufferSizeValue(arg.rotating);
hipblasltSetColdIterationsValue(arg.cold_iters);
hipblasltSetHotIterationsValue(arg.iters);
// integer_exact: skip gfx11 only for 16-bit A (fp16/bf16)—GPU vs CPU exact match unreliable there;
// f32/f64 (and TF32x1 f32 path) still run on Navi.
if(arg.initialization == hipblaslt_initialization::integer_exact)
{
const bool is_16bit = (tiA == HIP_R_16F || tiA == HIP_R_16BF);
if(hipblaslt_get_arch_major() == 11 && is_16bit)
{
hipblaslt_cout << "Skipping integer_exact on gfx11 for 16-bit float (fp16/bf16 A)"
<< std::endl;
return;
}
if(is_16bit)
{
// alpha=2: |2*dot|<=8K; beta=-2 adds 2*C. fp16 exact int ~2048 => K<=256 for both betas used
const int32_t k_limit
= (arg.alpha == 2.0f && (arg.beta == 0.0f || arg.beta == -2.0f)) ? 256 : 512;
const int32_t gemm_count = std::max(1, arg.grouped_gemm);
for(int32_t i = 0; i < gemm_count; i++)
{
if(arg.K[i] > k_limit)
{
hipblaslt_cout << "Skipping integer_exact: 16-bit format with K=" << arg.K[i]
<< " > " << k_limit << " (exact representability limit)"
<< std::endl;
return;
}
}
}
}
// FP16 full-matrix accumulator probe (see hipblaslt_init_device fp16_accumulator_probe).
if(arg.initialization == hipblaslt_initialization::fp16_accumulator_probe)
{
if(tiA != HIP_R_16F || tiB != HIP_R_16F || to != HIP_R_16F || arg.d_type != HIP_R_16F
|| arg.compute_type != HIPBLAS_COMPUTE_32F)
{
hipblaslt_cout
<< "Skipping fp16_accumulator_probe: requires f16 A/B/C/D and HIPBLAS_COMPUTE_32F"
<< std::endl;
return;
}
if(arg.transA != 'N' || arg.transB != 'N')
{
hipblaslt_cout << "Skipping fp16_accumulator_probe: only NN transposes supported"
<< std::endl;
return;
}
if(arg.grouped_gemm > 0)
{
hipblaslt_cout << "Skipping fp16_accumulator_probe: grouped_gemm not supported"
<< std::endl;
return;
}
if(arg.bias_vector || arg.activation_type != hipblaslt_activation_type::none || arg.use_e
|| arg.gradient || arg.scaleA != hipblaslt_scaling_format::none
|| arg.scaleB != hipblaslt_scaling_format::none || arg.scaleC || arg.scaleD || arg.scaleE
|| arg.scaleAlpha_vector || arg.amaxScaleA || arg.amaxScaleB || arg.amaxD)
{
hipblaslt_cout
<< "Skipping fp16_accumulator_probe: requires default epilogue (no bias, "
"activation, aux, or scaling)"
<< std::endl;
return;
}
if(arg.beta != 0.0f)
{
hipblaslt_cout << "Skipping fp16_accumulator_probe: requires beta == 0" << std::endl;
return;
}
const int32_t gemm_count_pe = std::max(1, arg.grouped_gemm);
for(int32_t i = 0; i < gemm_count_pe; i++)
{
if((arg.K[i] & 1) != 0)
{
hipblaslt_cout << "Skipping fp16_accumulator_probe: odd K not supported (K="
<< arg.K[i] << ")" << std::endl;
return;
}
}
}
// for all f8/bf8 cases including mix mode
if((realDataTypeSize(tiA) == 1 || realDataTypeSize(tiB) == 1) && tc != HIP_R_32I)
{
if(to == HIP_R_16BF || to == HIP_R_32F)
{
if(real_bias_type == HIP_R_16BF)
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_16BF, real_aux_type);
}
else
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_32F, real_aux_type);
}
}
else
{
if(real_bias_type == HIP_R_16F)
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_16F, real_aux_type);
}
else
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_32F, real_aux_type);
}
}
}
else if(to == HIP_R_16F)
{
if(real_bias_type == HIP_R_16F)
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_16F, real_aux_type);
}
else
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_32F, real_aux_type);
}
}
else if(to == HIP_R_16BF)
{
if(real_bias_type == HIP_R_16BF)
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_16BF, real_aux_type);
}
else
{
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, HIP_R_32F, real_aux_type);
}
}
else if(to == HIP_R_32F || to == HIP_R_32I || to == HIP_R_8I || to == HIP_R_64F
|| to == HIP_C_32F || to == HIP_C_64F)
{
//set Tbias to To
return testing_matmul_with_bias(
arg_revised, tiA, tiB, to, tc, tciA, tciB, to, real_aux_type);
}
// shouldn't arrive here
hipblaslt_test_invalid{}(arg);
return;
}
void testing_matmul_with_bias(const Arguments& arg,
hipDataType TiA,
hipDataType TiB,
hipDataType To,
hipDataType Tc,
hipDataType TciA,
hipDataType TciB,
hipDataType Tbias,
hipDataType Taux)
{
double gpu_time_used, cpu_time_used, gpu_mem_gbytes;
gpu_time_used = cpu_time_used = gpu_mem_gbytes = 0.0;
bool HMM = arg.HMM;
hipblaslt_local_handle handle{arg};
hipStream_t stream;
CHECK_HIP_ERROR(hipStreamCreate(&stream));
hipEvent_t event_gpu_time_start, event_gpu_time_end;
CHECK_HIP_ERROR(hipEventCreate(&event_gpu_time_start));
CHECK_HIP_ERROR(hipEventCreate(&event_gpu_time_end));
hipblasOperation_t transA(char_to_hipblas_operation(arg.transA));
hipblasOperation_t transB(char_to_hipblas_operation(arg.transB));
// If input type is complex then alpha is set to complex datatype else compute type
hipDataType Talpha = (TiA == HIP_C_32F || TiA == HIP_C_64F) ? TiA : Tc;
bool do_grouped_gemm = arg.grouped_gemm > 0;
int32_t gemm_count = std::max(1, arg.grouped_gemm);
// (batch_mode value : 0 for Strided Batched Gemm, 1 for General Batched Gemm)
hipblasLtBatchMode_t batchMode = static_cast<hipblasLtBatchMode_t>(arg.batch_mode);
int64_t rotating = arg.rotating * 1024 * 1024;
std::vector<int64_t> M(gemm_count), N(gemm_count), K(gemm_count), lda(gemm_count),
ldb(gemm_count), ldc(gemm_count), ldd(gemm_count), lde(gemm_count);
std::vector<computeTypeInterface> h_alpha(gemm_count, computeTypeInterface{}),
h_beta(gemm_count, computeTypeInterface{});
std::vector<int64_t> A_row(gemm_count), A_col(gemm_count), B_row(gemm_count), B_col(gemm_count);
std::vector<int64_t> stride_a(gemm_count), stride_da(gemm_count), stride_b(gemm_count),
stride_db(gemm_count), stride_c(gemm_count), stride_d(gemm_count), stride_e(gemm_count);
std::vector<bool> do_batched(gemm_count), epilogue_on(gemm_count, false);
std::vector<int> num_batches(gemm_count);
std::vector<size_t> size_A(gemm_count), size_dA(gemm_count), size_B(gemm_count),
size_dB(gemm_count), size_C(gemm_count), size_D(gemm_count), size_D_copy(gemm_count),
size_E(gemm_count), size_bias(gemm_count), size_scaleAlphaVec(gemm_count),
size_scaleAVec(gemm_count), size_scaleBVec(gemm_count);
std::vector<hipblasLtMatrixLayout_t> matA(gemm_count), matB(gemm_count), matC(gemm_count),
matD(gemm_count);
std::vector<std::vector<hipblasLtMatmulDesc_t>> matmul;
std::vector<hipblasLtEpilogue_t> epilogue(gemm_count, HIPBLASLT_EPILOGUE_DEFAULT);
std::vector<float> act0(gemm_count), act1(gemm_count);
std::vector<HipDeviceBuffer> dA, dB, dC, dD, dE, dBias;
std::vector<HipDeviceBuffer>* dDp;
std::vector<HipDeviceBuffer> dScaleAlphaVec, dScaleA, dScaleB, dScaleC, dScaleD, dScaleE,
dAmaxD;
std::vector<HipHostBuffer> hE, hE_gold, hBias, hBias_gold;
std::vector<HipHostBuffer> hA, hB, hC, hD_gold, hD_1;
std::vector<HipHostBuffer> hScaleAlphaVec, hScaleA, hScaleB, hScaleC, hScaleD, hScaleE,
hAmaxD_gold, hAmaxD, hD_gold_epl, hD_gold_ScaleAlpha, hBias_gold_epl;
// These two vectors store the float values of MX data. mxDataGenerator
// can generate MX data and return the corresponding float values. The float
// values can be directly used for CPU verification (cblas_gemm) instead
// of converting the MX data to float again.
std::vector<std::vector<float>> refA, refB;
std::vector<void*> alpha_in(gemm_count);
bool do_swizzle_a = arg.swizzle_a && isSwizzleSupported(TiA);
bool do_swizzle_b = arg.swizzle_b && isSwizzleSupported(TiB);
bool mx_use_rocroller = MXUseRocroller();
// Need to split into two for loop to calculate the rotating buffer
int64_t totalRotatingSizeNeeded = 0;
for(int i = 0; i < gemm_count; i++)
{
M[i] = arg.M[i];
N[i] = arg.N[i];
K[i] = arg.K[i];
set_alpha_type(h_alpha[i], arg, Tc, TiA);
set_beta_type(h_beta[i], arg, Tc, TiA);
lda[i] = arg.lda[i];
ldb[i] = arg.ldb[i];
ldc[i] = arg.ldc[i];
ldd[i] = arg.ldd[i];
lde[i] = arg.lde[i];
A_row[i] = transA == HIPBLAS_OP_N ? M[i] : K[i];
A_col[i] = transA == HIPBLAS_OP_N ? K[i] : M[i];
B_row[i] = transB == HIPBLAS_OP_N ? K[i] : N[i];
B_col[i] = transB == HIPBLAS_OP_N ? N[i] : K[i];
do_batched[i] = (arg.batch_count > 1);
num_batches[i] = (do_batched[i] ? arg.batch_count : 1);
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
stride_a[i] = do_batched[i] ? arg.stride_a[i] : lda[i] * A_col[i];
stride_b[i] = do_batched[i] ? arg.stride_b[i] : ldb[i] * B_col[i];
stride_c[i] = do_batched[i] ? arg.stride_c[i] : ldc[i] * N[i];
stride_d[i] = do_batched[i] ? arg.stride_c[i] : ldd[i] * N[i];
stride_e[i] = do_batched[i] ? arg.stride_e[i] : lde[i] * N[i];
}
else
{
// Keeping the stride logic same as how it is handled in Grouped GEMM case
stride_a[i] = lda[i] * A_col[i];
stride_b[i] = ldb[i] * B_col[i];
stride_c[i] = ldc[i] * N[i];
stride_d[i] = ldd[i] * N[i];
stride_e[i] = lde[i] * N[i];
}
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
size_A[i] = stride_a[i] == 0 ? lda[i] * A_col[i] * num_batches[i]
: lda[i] <= stride_a[i] ? stride_a[i] * num_batches[i]
: lda[i] * A_col[i];
}
else
{
size_A[i] = stride_a[i];
}
// for (!do_swizzle_a) case, we can use size_dA and stride_da instead of size_A and stride_a
size_dA[i] = size_A[i];
stride_da[i] = stride_a[i];
if(do_swizzle_a)
{
size_t MiM = 16, MiK = 0, __ = 0, PackK = 0;
calculateKforSwizzling(TiA, arg, MiK, __, PackK);
size_t K_block = MiK * PackK;
int64_t stride_swizzle
= ((M[i] + MiM - 1) / MiM) * MiM * ((K[i] + K_block - 1) / K_block) * K_block;
if(do_batched[i] && stride_a[i] != 0)
{
stride_da[i] = stride_swizzle;
//TODO: support arbitrary stride_a for both hipblaslt-bench and hipblaslt-test when swizzled
if(stride_a[i] != lda[i] * A_col[i] && stride_a[i] != stride_swizzle)
hipblaslt_cerr << "Warning: swizzle_a does not yet support arbitrary stride_a!"
<< std::endl;
}
size_dA[i] = (batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) ? stride_swizzle : (num_batches[i] * stride_swizzle);
}
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
size_B[i] = stride_b[i] == 0 ? ldb[i] * B_col[i] * num_batches[i]
: ldb[i] <= stride_b[i] ? stride_b[i] * num_batches[i]
: ldb[i] * B_col[i];
}
else
{
size_B[i] = stride_b[i];
}
// for (!do_swizzle_b) case, we can use size_dB and stride_db instead of size_B and stride_b
size_dB[i] = size_B[i];
stride_db[i] = stride_b[i];
if(do_swizzle_b)
{
size_t MiN = 16, MiK = 0, __ = 0, PackK = 0;
calculateKforSwizzling(TiB, arg, MiK, __, PackK);
size_t K_block = MiK * PackK;
int64_t stride_swizzle
= ((N[i] + MiN - 1) / MiN) * MiN * ((K[i] + K_block - 1) / K_block) * K_block;
if(do_batched[i] && stride_b[i] != 0)
{
stride_db[i] = stride_swizzle;
//TODO: support arbitrary stride_b for both hipblaslt-bench and hipblaslt-test when swizzled
if(stride_b[i] != ldb[i] * B_col[i] && stride_b[i] != stride_swizzle)
hipblaslt_cerr << "Warning: swizzle_b does not yet support arbitrary stride_b!"
<< std::endl;
}
size_dB[i] = (batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) ? stride_swizzle : (num_batches[i] * stride_swizzle);
}
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
size_C[i] = stride_c[i] == 0 ? ldc[i] * N[i] * num_batches[i]
: ldc[i] <= stride_c[i] ? stride_c[i] * num_batches[i]
: ldc[i] * N[i];
size_D[i] = stride_d[i] == 0 ? ldd[i] * N[i] * num_batches[i]
: ldd[i] <= stride_d[i] ? stride_d[i] * num_batches[i]
: ldd[i] * N[i];
size_E[i] = arg.use_e ? (stride_e[i] == 0 ? lde[i] * N[i] * num_batches[i]
: lde[i] <= stride_e[i] ? stride_e[i] * num_batches[i]
: lde[i] * N[i])
: 0;
}
else
{
size_C[i] = ldc[i] * N[i];
size_D[i] = ldd[i] * N[i];
size_E[i] = lde[i] * N[i];
}
if(arg.c_equal_d)
{
ldd[i] = arg.ldc[i];
stride_d[i] = stride_c[i];
size_D[i] = size_C[i];
}
size_D_copy[i] = (arg.unit_check || arg.norm_check || arg.allclose_check) ? size_D[i] : 0;
size_scaleAlphaVec[i] = arg.scaleAlpha_vector ? M[i] : 0;
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
if(arg.scaleA == hipblaslt_scaling_format::Scalar)
size_scaleAVec[i] = 1;
else if(arg.scaleA == hipblaslt_scaling_format::Vector)
size_scaleAVec[i] = M[i];
else if(isBlockScaling(arg.scaleA))
{
if(!mx_use_rocroller)
{
// Account for padding in the swizzled MX layout
size_t MXBlock_A = blockSize(arg.scaleA);
size_t dimk = 128 / MXBlock_A;
size_t scaleA_r = A_row[i] / ((transA == HIPBLAS_OP_T) ? MXBlock_A : 1);
size_t scaleA_c = A_col[i] / ((transA == HIPBLAS_OP_T) ? 1 : MXBlock_A);
bool kAlongRowsA = (transA == HIPBLAS_OP_T);
size_t kDim = kAlongRowsA ? scaleA_r : scaleA_c;
size_t mnDim = kAlongRowsA ? scaleA_c : scaleA_r;
size_t padDim = kAlongRowsA ? kDim : mnDim;
size_t paddedDim = (padDim + dimk - 1) / dimk * dimk;
size_scaleAVec[i] = kAlongRowsA ? (mnDim * paddedDim) : (kDim * paddedDim);
}
else
{
size_scaleAVec[i] = scaleBufferSize(A_row[i], A_col[i], arg.scaleA);
}
}
else
size_scaleAVec[i] = 0;
if(arg.scaleB == hipblaslt_scaling_format::Scalar)
size_scaleBVec[i] = 1;
else if(arg.scaleB == hipblaslt_scaling_format::Vector)
size_scaleBVec[i] = N[i];
else if(isBlockScaling(arg.scaleB))
{
if(!mx_use_rocroller)
{
// Account for padding in the swizzled MX layout
size_t MXBlock_B = blockSize(arg.scaleB);
size_t dimk = 128 / MXBlock_B;
size_t scaleB_r = B_row[i] / ((transB == HIPBLAS_OP_T) ? 1 : MXBlock_B);
size_t scaleB_c = B_col[i] / ((transB == HIPBLAS_OP_T) ? MXBlock_B : 1);
bool kAlongRowsB = (transB == HIPBLAS_OP_N);
size_t kDim = kAlongRowsB ? scaleB_r : scaleB_c;
size_t mnDim = kAlongRowsB ? scaleB_c : scaleB_r;
size_t padDim = kAlongRowsB ? kDim : mnDim;
size_t paddedDim = (padDim + dimk - 1) / dimk * dimk;
size_scaleBVec[i] = kAlongRowsB ? (mnDim * paddedDim) : (kDim * paddedDim);
}
else
{
size_scaleBVec[i] = scaleBufferSize(B_row[i], B_col[i], arg.scaleB);
}
}
else
size_scaleBVec[i] = 0;
}
else
{
if(arg.scaleA == hipblaslt_scaling_format::Scalar)
size_scaleAVec[i] = 1;
else if(arg.scaleA == hipblaslt_scaling_format::none)
size_scaleAVec[i] = 0;
else
{
hipblaslt_cout << "Only Tensorwide scaling is supported for General Batched GEMM"
<< std::endl;
return;
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar)
size_scaleBVec[i] = 1;
else if(arg.scaleB == hipblaslt_scaling_format::none)
size_scaleBVec[i] = 0;
else
{
hipblaslt_cout << "Only Tensorwide scaling is supported for General Batched GEMM"
<< std::endl;
return;
}
}
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
if(arg.bias_vector)
{
if(arg.bias_source == hipblaslt_bias_source::a
|| arg.bias_source == hipblaslt_bias_source::d)
size_bias[i] = M[i];
else if(arg.bias_source == hipblaslt_bias_source::b)
size_bias[i] = N[i];
if(arg.bias_stride > 0)
{
size_bias[i] = arg.bias_stride * num_batches[i];
}
}
else
{
size_bias[i] = 0;
}
}
else
{
size_bias[i] = 0;
}
auto biasSize = size_bias[i] * realDataTypeSize(Tbias);
int64_t sizeC = get_computeInterface(h_beta[i], Tc) == 0 ? 0 : size_C[i] * sizeof(To);
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
totalRotatingSizeNeeded
+= size_dA[i] * realDataTypeSize(TiA) + size_dB[i] * realDataTypeSize(TiB) + sizeC
+ size_D[i] * realDataTypeSize(To) + size_E[i] * realDataTypeSize(To) + biasSize
+ size_scaleAlphaVec[i] * realDataTypeSize(Talpha)
+ size_scaleAVec[i] * realDataTypeSize(Talpha)
+ size_scaleBVec[i] * realDataTypeSize(Talpha);
}
else
{
// For General Batched GEMM, the Matrices aren't stored in a continuous buffer across batches.
// Hence size_dA doesn't account for all batches.
totalRotatingSizeNeeded += size_dA[i] * realDataTypeSize(TiA) * num_batches[i]
+ size_dB[i] * realDataTypeSize(TiB) * num_batches[i]
+ sizeC * num_batches[i]
+ size_D[i] * realDataTypeSize(To) * num_batches[i]
+ biasSize + size_scaleAlphaVec[i] * realDataTypeSize(Talpha)
+ size_scaleAVec[i] * realDataTypeSize(Talpha)
+ size_scaleBVec[i] * realDataTypeSize(Talpha);
}
}
gpu_mem_gbytes = static_cast<double>(totalRotatingSizeNeeded) / (1024 * 1024 * 1024);
// Calculating block count
auto plan = hipblaslt_bench::compute_rotating_buffer_plan(
arg.adaptive, arg.max_iters, arg.cold_iters, arg.iters, rotating, totalRotatingSizeNeeded);
int32_t block_count = plan.block_count;
if(rotating > 0)
{
hipblaslt_cout << "Rotating buffer " << rotating / (1024 * 1024) << " MiB. "
<< "Needed Size: " << totalRotatingSizeNeeded / (1024 * 1024) << " MiB. "
<< "Needed block count: " << block_count;
if(plan.capped)
hipblaslt_cout << " (Capped to max iters: " << plan.iter_cap << ")";
hipblaslt_cout << std::endl;
}
// Calculating block count end
matmul.resize(block_count, std::vector<hipblasLtMatmulDesc_t>(gemm_count));
for(int i = 0; i < gemm_count; i++)
{
CHECK_HIPBLASLT_ERROR(
hipblasLtMatrixLayoutCreate(&(matA[i]), arg.a_type, A_row[i], A_col[i], lda[i]));
CHECK_HIPBLASLT_ERROR(
hipblasLtMatrixLayoutCreate(&(matB[i]), arg.b_type, B_row[i], B_col[i], ldb[i]));
CHECK_HIPBLASLT_ERROR(
hipblasLtMatrixLayoutCreate(&(matC[i]), arg.c_type, M[i], N[i], ldc[i]));
CHECK_HIPBLASLT_ERROR(
hipblasLtMatrixLayoutCreate(&(matD[i]), arg.d_type, M[i], N[i], ldd[i]));
if(do_swizzle_a)
{
hipblasLtOrder_t orderA = orderForDatatype(TiA);
CHECK_HIPBLASLT_ERROR(hipblasLtMatrixLayoutSetAttribute(
matA[i], HIPBLASLT_MATRIX_LAYOUT_ORDER, &orderA, sizeof(orderA)));
}
if(do_swizzle_b)
{
hipblasLtOrder_t orderB = orderForDatatype(TiB);
CHECK_HIPBLASLT_ERROR(hipblasLtMatrixLayoutSetAttribute(
matB[i], HIPBLASLT_MATRIX_LAYOUT_ORDER, &orderB, sizeof(orderB)));
}
if(do_batched[i] || batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matA[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_COUNT, &(num_batches[i]), sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matB[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_COUNT, &(num_batches[i]), sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matC[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_COUNT, &(num_batches[i]), sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matD[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_COUNT, &(num_batches[i]), sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(matA[i],
HIPBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET,
&(stride_da[i]),
sizeof(int64_t)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(matB[i],
HIPBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET,
&(stride_db[i]),
sizeof(int64_t)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(matC[i],
HIPBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET,
&(stride_c[i]),
sizeof(int64_t)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(matD[i],
HIPBLASLT_MATRIX_LAYOUT_STRIDED_BATCH_OFFSET,
&(stride_d[i]),
sizeof(int64_t)),
HIPBLAS_STATUS_SUCCESS);
}
if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matA[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_MODE, &batchMode, sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matB[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_MODE, &batchMode, sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matC[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_MODE, &batchMode, sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatrixLayoutSetAttribute(
matD[i], HIPBLASLT_MATRIX_LAYOUT_BATCH_MODE, &batchMode, sizeof(int)),
HIPBLAS_STATUS_SUCCESS);
}
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescCreate(&(matmul[0][i]), arg.compute_type, arg.scale_type));
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(
matmul[0][i], HIPBLASLT_MATMUL_DESC_COMPUTE_INPUT_TYPE_A_EXT, &TciA, sizeof(void*)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(
matmul[0][i], HIPBLASLT_MATMUL_DESC_COMPUTE_INPUT_TYPE_B_EXT, &TciB, sizeof(void*)),
HIPBLAS_STATUS_SUCCESS);
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], HIPBLASLT_MATMUL_DESC_TRANSA, &transA, sizeof(int32_t)));
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], HIPBLASLT_MATMUL_DESC_TRANSB, &transB, sizeof(int32_t)));
// Forward CLI knobs from hipblaslt-bench into the matmul descriptor.
{
int32_t sm = hipblaslt_bench_options::sm_count_target();
if(sm != 0)
{
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i],
HIPBLASLT_MATMUL_DESC_SM_COUNT_TARGET,
&sm,
sizeof(sm)));
}
int32_t dyn = hipblaslt_bench_options::streamk_tile_scheduling_mode();
if(dyn >= 0)
{
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i],
HIPBLASLT_MATMUL_DESC_STREAMK_TILE_SCHEDULING_EXT,
&dyn,
sizeof(dyn)));
}
}
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
if(arg.bias_vector)
{
epilogue_on[i] = true;
switch(arg.activation_type)
{
case hipblaslt_activation_type::relu:
epilogue[i] = HIPBLASLT_EPILOGUE_RELU_BIAS;
break;
case hipblaslt_activation_type::gelu:
epilogue[i] = HIPBLASLT_EPILOGUE_GELU_BIAS;
break;
case hipblaslt_activation_type::swish:
epilogue[i] = HIPBLASLT_EPILOGUE_SWISH_BIAS_EXT;
break;
case hipblaslt_activation_type::clamp:
epilogue[i] = HIPBLASLT_EPILOGUE_CLAMP_BIAS_EXT;
break;
default:
epilogue[i] = HIPBLASLT_EPILOGUE_BIAS;
break;
}
}
else
{
switch(arg.activation_type)
{
case hipblaslt_activation_type::relu:
epilogue[i] = HIPBLASLT_EPILOGUE_RELU;
epilogue_on[i] = true;
break;
case hipblaslt_activation_type::gelu:
epilogue[i] = HIPBLASLT_EPILOGUE_GELU;
epilogue_on[i] = true;
break;
case hipblaslt_activation_type::swish:
epilogue[i] = HIPBLASLT_EPILOGUE_SWISH_EXT;
epilogue_on[i] = true;
break;
case hipblaslt_activation_type::clamp:
epilogue[i] = HIPBLASLT_EPILOGUE_CLAMP_EXT;
epilogue_on[i] = true;
break;
default:
break;
}
}
if(epilogue_on[i])
{
act0[i] = arg.activation_arg1;
act1[i] = arg.activation_arg2;
}
if(arg.gradient)
{
switch(epilogue[i])
{
case HIPBLASLT_EPILOGUE_BIAS:
{
switch(arg.bias_source)
{
case hipblaslt_bias_source::a:
epilogue[i] = HIPBLASLT_EPILOGUE_BGRADA;
break;
case hipblaslt_bias_source::b:
epilogue[i] = HIPBLASLT_EPILOGUE_BGRADB;
break;
default:
break;
}
}
break;
case HIPBLASLT_EPILOGUE_GELU:
CHECK_SUCCESS(arg.use_e
&& "Must enable use e if gradient is enabled with gelu.");
epilogue[i] = HIPBLASLT_EPILOGUE_DGELU;
break;
case HIPBLASLT_EPILOGUE_GELU_BIAS:
CHECK_SUCCESS(arg.use_e
&& "Must enable use e if gradient is enabled with gelu.");
epilogue[i] = HIPBLASLT_EPILOGUE_DGELU_BGRAD;
break;
case HIPBLASLT_EPILOGUE_RELU:
CHECK_SUCCESS(arg.use_e
&& "Must enable use e if gradient is enabled with relu.");
epilogue[i] = HIPBLASLT_EPILOGUE_DRELU;
break;
case HIPBLASLT_EPILOGUE_RELU_BIAS:
CHECK_SUCCESS(arg.use_e
&& "Must enable use e if gradient is enabled with relu.");
epilogue[i] = HIPBLASLT_EPILOGUE_DRELU_BGRAD;
break;
default:
break;
}
}
if(arg.use_e)
{
switch(epilogue[i])
{
case HIPBLASLT_EPILOGUE_RELU:
epilogue[i] = HIPBLASLT_EPILOGUE_RELU_AUX;
break;
case HIPBLASLT_EPILOGUE_RELU_BIAS:
epilogue[i] = HIPBLASLT_EPILOGUE_RELU_AUX_BIAS;
break;
case HIPBLASLT_EPILOGUE_GELU:
epilogue[i] = HIPBLASLT_EPILOGUE_GELU_AUX;
break;
case HIPBLASLT_EPILOGUE_GELU_BIAS:
epilogue[i] = HIPBLASLT_EPILOGUE_GELU_AUX_BIAS;
break;
case HIPBLASLT_EPILOGUE_CLAMP_EXT:
epilogue[i] = HIPBLASLT_EPILOGUE_CLAMP_AUX_EXT;
break;
case HIPBLASLT_EPILOGUE_CLAMP_BIAS_EXT:
epilogue[i] = HIPBLASLT_EPILOGUE_CLAMP_AUX_BIAS_EXT;
break;
case HIPBLASLT_EPILOGUE_DGELU:
case HIPBLASLT_EPILOGUE_DGELU_BGRAD:
// DGELU_AUX and DGELU_AUX_BGRAD already use E
break;
case HIPBLASLT_EPILOGUE_DRELU:
case HIPBLASLT_EPILOGUE_DRELU_BGRAD:
// DRELU_AUX and DRELU_AUX_BGRAD already use E
break;
default:
hipblaslt_cerr << "The activation type " << epilogue[i]
<< " does not support '--use_e'.\n";
CHECK_SUCCESS(false);
break;
}
}
if(arg.scaleAlpha_vector)
{
epilogue_on[i] = true;
}
// allocate memory on device
dA.emplace_back(TiA, size_dA[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
dB.emplace_back(TiB, size_dB[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
dC.emplace_back(To, size_C[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
if(!arg.c_equal_d)
{
dD.emplace_back(To, size_D[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
dDp = &dD;
}
else
dDp = &dC;
if(size_bias[i] * block_count != 0)
{
dBias.emplace_back(Tbias, size_bias[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleAlpha_vector)
{
dScaleAlphaVec.emplace_back(Talpha, size_scaleAlphaVec[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.use_e)
{
dE.emplace_back(Taux, size_E[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleA == hipblaslt_scaling_format::Scalar
|| arg.scaleA == hipblaslt_scaling_format::Vector)
{
dScaleA.emplace_back(Talpha, size_scaleAVec[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
else if(isBlockScaling(arg.scaleA))
{
// For MX format, use uint8_t for the scale (E8M0), allocate for all batches
dScaleA.emplace_back(HIP_R_8U, size_scaleAVec[i] * num_batches[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar
|| arg.scaleB == hipblaslt_scaling_format::Vector)
{
dScaleB.emplace_back(Talpha, size_scaleBVec[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
else if(isBlockScaling(arg.scaleB))
{
// For MX format, use uint8_t for the scale (E8M0), allocate for all batches
dScaleB.emplace_back(HIP_R_8U, size_scaleBVec[i] * num_batches[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleC)
{
dScaleC.emplace_back(Talpha, 1, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleD)
{
dScaleD.emplace_back(Talpha, 1, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.amaxD)
{
epilogue_on[i] = true;
dAmaxD.emplace_back(Talpha, 1, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleE)
{
dScaleE.emplace_back(Talpha, 1, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
// Naming: dX is in GPU (device) memory. hK is in CPU (host) memory
hA.emplace_back(TiA, size_A[i]);
hB.emplace_back(TiB, size_B[i]);
hC.emplace_back(To, size_C[i]);
hD_gold.emplace_back(To, size_D_copy[i]);
hD_1.emplace_back(To, size_D_copy[i]);
if(size_bias[i] * block_count != 0)
{
hBias.emplace_back(Tbias, size_bias[i]);
hBias_gold.emplace_back(Tbias, size_bias[i]);
}
hD_gold_epl.emplace_back(Talpha, size_D_copy[i]);
hD_gold_ScaleAlpha.emplace_back(Talpha, size_D_copy[i]);
hBias_gold_epl.emplace_back(Talpha, size_D_copy[i]); // Reduction for matrix D
if(arg.scaleAlpha_vector)
hScaleAlphaVec.emplace_back(Talpha, size_scaleAlphaVec[i]);
if(arg.scaleA == hipblaslt_scaling_format::Scalar
|| arg.scaleA == hipblaslt_scaling_format::Vector)
{
hScaleA.emplace_back(Talpha, size_scaleAVec[i]);
}
else if(isBlockScaling(arg.scaleA))
{
hScaleA.emplace_back(HIP_R_8U, size_scaleAVec[i] * num_batches[i]);
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar
|| arg.scaleB == hipblaslt_scaling_format::Vector)
{
hScaleB.emplace_back(Talpha, size_scaleBVec[i]);
}
else if(isBlockScaling(arg.scaleB))
{
hScaleB.emplace_back(HIP_R_8U, size_scaleBVec[i] * num_batches[i]);
}
if(arg.scaleC)
hScaleC.emplace_back(Talpha, 1);
if(arg.scaleD)
hScaleD.emplace_back(Talpha, 1);
if(arg.amaxD)
{
hAmaxD_gold.emplace_back(Talpha, 1);
hAmaxD.emplace_back(Talpha, 1);
}
if(arg.scaleE)
hScaleE.emplace_back(Talpha, 1);
if(arg.use_e)
{
hE.emplace_back(Taux, size_E[i]);
if(!arg.gradient)
{
hE_gold.emplace_back(Taux, size_E[i]);
}
}
}
else
{
for(int batchCount = 0; batchCount < arg.batch_count; batchCount++)
{
// allocate memory on device
dA.emplace_back(TiA, size_dA[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
dB.emplace_back(TiB, size_dB[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
dC.emplace_back(To, size_C[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
if(!arg.c_equal_d)
{
dD.emplace_back(To, size_D[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
dDp = &dD;
}
else
dDp = &dC;
if(size_bias[i] * block_count != 0)
{
dBias.emplace_back(Tbias, size_bias[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleAlpha_vector)
{
hipblaslt_cout << "General Batched GEMM does not support scaleAlpha_vector."
<< std::endl;
return;
}
if(arg.use_e)
{
hipblaslt_cout << "General Batched GEMM does not support use_e." << std::endl;
return;
}
// Naming: dX is in GPU (device) memory. hK is in CPU (host) memory
hA.emplace_back(TiA, size_A[i]);
hB.emplace_back(TiB, size_B[i]);
hC.emplace_back(To, size_C[i]);
hD_gold.emplace_back(To, size_D_copy[i]);
hD_1.emplace_back(To, size_D_copy[i]);
if(size_bias[i] * block_count != 0)
{
hBias.emplace_back(Tbias, size_bias[i]);
hBias_gold.emplace_back(Tbias, size_bias[i]);
}
}
if(arg.scaleA == hipblaslt_scaling_format::Scalar
|| arg.scaleA == hipblaslt_scaling_format::none)
{
dScaleA.emplace_back(Talpha, size_scaleAVec[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
else
{
hipblaslt_cout << "General Batched GEMM only support Tensorwide scaling."
<< std::endl;
return;
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar
|| arg.scaleB == hipblaslt_scaling_format::none)
{
dScaleB.emplace_back(Talpha, size_scaleBVec[i] * block_count, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
else
{
hipblaslt_cout << "General Batched GEMM only support Tensorwide scaling."
<< std::endl;
return;
}
if(arg.scaleC)
{
dScaleC.emplace_back(Talpha, 1, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.scaleD)
{
dScaleD.emplace_back(Talpha, 1, HMM);
CHECK_DEVICE_ALLOCATION(hipGetLastError());
}
if(arg.amaxD)
{
hipblaslt_cout << "General Batched GEMM doesn't support Epilogues."
<< "Only Scaling and Quantization is supported for post processing"
<< std::endl;
return;
}
if(arg.scaleE)
{
hipblaslt_cout << "General Batched GEMM doesn't support Epilogues."
<< "Only Scaling and Quantization is supported for post processing"
<< std::endl;
return;
}
if(arg.scaleAlpha_vector)
{
hipblaslt_cout << "General Batched GEMM does not support scaleAlpha_vector."
<< std::endl;
return;
}
if(arg.scaleA == hipblaslt_scaling_format::Scalar
|| arg.scaleA == hipblaslt_scaling_format::none)
{
hScaleA.emplace_back(Talpha, size_scaleAVec[i]);
}
else
{
hipblaslt_cout << "General Batched GEMM only support Tensorwide scaling."
<< std::endl;
return;
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar
|| arg.scaleB == hipblaslt_scaling_format::none)
{
hScaleB.emplace_back(Talpha, size_scaleBVec[i]);
}
else
{
hipblaslt_cout << "General Batched GEMM only support Tensorwide scaling."
<< std::endl;
return;
}
if(arg.scaleC)
hScaleC.emplace_back(Talpha, 1);
if(arg.scaleD)
hScaleD.emplace_back(Talpha, 1);
}
hipblaslt_seedrand();
// For ULP validation, force hpl / trig_float A/B/C inputs to be
// positive-only so the reference dot products do not cancel toward zero
// (near-zero outputs inflate the per-element ULP error spuriously).
set_ulp_positive_init_state(arg.ulp_check);
#if HIPBLASLT_ENABLE_MXDATAGENERATOR
hipDeviceProp_t mxProp{};
if(isBlockScaling(arg.scaleA) || isBlockScaling(arg.scaleB))
CHECK_HIP_ERROR(hipGetDeviceProperties(&mxProp, 0));
#endif
size_t scaleA_row = ((transA == HIPBLAS_OP_T) ? blockSize(arg.scaleA) : 1);
size_t scaleA_col = ((transA == HIPBLAS_OP_T) ? 1 : blockSize(arg.scaleA));
if(isBlockScaling(arg.scaleA))
{
#if HIPBLASLT_ENABLE_MXDATAGENERATOR
if(arg.initialization != hipblaslt_initialization::hpl
&& arg.initialization != hipblaslt_initialization::trig_float
&& arg.initialization != hipblaslt_initialization::uniform_01
&& arg.initialization != hipblaslt_initialization::zero
&& arg.initialization != hipblaslt_initialization::norm_dist
&& arg.initialization != hipblaslt_initialization::rand_int
&& arg.initialization != hipblaslt_initialization::uniform_low_precision)
{
#ifdef GOOGLE_TEST
GTEST_SKIP() << "unsupported MX initialization: "
<< hipblaslt_initialization2string(arg.initialization);
#else
hipblaslt_cout << "Initialization of microscaling data only allows hpl, trig_float, "
"uniform_01, zero, norm_dist, rand_int or uniform_low_precision, not "
<< hipblaslt_initialization2string(arg.initialization) << std::endl;
return;
#endif
}
if(arg.algo_method == 1)
{
#ifdef GOOGLE_TEST
GTEST_SKIP() << "MX data types do not support algorithm \"all\"";
#else
hipblaslt_cout << "MX data types do not support algorithm \"all\"" << std::endl;
return;
#endif
}
MXScaleLayout const scaleLayoutA
= mxScaleLayoutForFormat(arg.scaleA, mxProp.gcnArchName);
size_t dataBatchBytesA = (num_batches[i] > 1) ? elementsToBytes(stride_a[i], TiA) : 0;
size_t scaleBatchBytesA = (num_batches[i] > 1) ? size_scaleAVec[i] : 0;
std::vector<float> refAAll;
refAAll.reserve(static_cast<size_t>(A_row[i]) * A_col[i] * num_batches[i]);
for(int64_t b = 0; b < num_batches[i]; b++)
{
auto* dataPtrA = reinterpret_cast<uint8_t*>(hA[i].buf()) + b * dataBatchBytesA;
auto* scalePtrA = reinterpret_cast<uint8_t*>(hScaleA[i].buf()) + b * scaleBatchBytesA;
auto batchRef
= generateMXInput(TiA,
scaleDataType(arg.scaleA),
dataPtrA,
scalePtrA,
A_row[i],
A_col[i],
lda[i],
transA == HIPBLAS_OP_T,
blockSize(arg.scaleA),
1,
/*isMatrixA=*/true,
scaleLayoutA,
hipblaslt_initialization2string(arg.initialization),
/*min_val=*/-1.0f,
/*max_val=*/1.0f);
refAAll.insert(refAAll.end(), batchRef.begin(), batchRef.end());
}
refA.emplace_back(std::move(refAAll));
CHECK_HIP_ERROR(synchronize(dA[i], hA[i], block_count));
CHECK_HIP_ERROR(synchronize(dScaleA[i], hScaleA[i], block_count));
#else
#ifdef GOOGLE_TEST
GTEST_SKIP() << "MX data initialization requires HIPBLASLT_ENABLE_MXDATAGENERATOR=ON at build time";
#else
hipblaslt_cout << "MX data initialization requires HIPBLASLT_ENABLE_MXDATAGENERATOR=ON at build time"
<< std::endl;
return;
#endif
#endif
}
else
{
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
hipblaslt_init_device(ABC_dims::A,
arg.initialization,
alpha_isnan_type(arg, Talpha),
dA[i].buf(),
A_row[i],
A_col[i],
(do_swizzle_a) ? A_row[i] : lda[i],
TiA,
(do_swizzle_a && stride_a[i] != 0) ? A_row[i] * A_col[i]
: stride_a[i],
num_batches[i]);
}
else
{
for(int batchCount = 0; batchCount < num_batches[i]; batchCount++)
{
hipblaslt_init_device(ABC_dims::A,
arg.initialization,
alpha_isnan_type(arg, Talpha),
dA[batchCount].buf(),
A_row[i],
A_col[i],
(do_swizzle_a) ? A_row[i] : lda[i],
TiA,
(do_swizzle_a && stride_a[i] != 0) ? A_row[i] * A_col[i]
: stride_a[i],
1);
}
}
}
size_t scaleB_row = ((transB == HIPBLAS_OP_T) ? 1 : blockSize(arg.scaleB));
size_t scaleB_col = ((transB == HIPBLAS_OP_T) ? blockSize(arg.scaleB) : 1);
if(isBlockScaling(arg.scaleB))
{
#if HIPBLASLT_ENABLE_MXDATAGENERATOR
// MX B always goes through mxDataGenerator (mirrors the A side above).
if(arg.initialization != hipblaslt_initialization::hpl
&& arg.initialization != hipblaslt_initialization::trig_float
&& arg.initialization != hipblaslt_initialization::uniform_01
&& arg.initialization != hipblaslt_initialization::zero
&& arg.initialization != hipblaslt_initialization::norm_dist
&& arg.initialization != hipblaslt_initialization::rand_int
&& arg.initialization != hipblaslt_initialization::uniform_low_precision)
{
#ifdef GOOGLE_TEST
GTEST_SKIP() << "unsupported MX initialization: "
<< hipblaslt_initialization2string(arg.initialization);
#else
hipblaslt_cout << "Initialization of microscaling data only allows hpl, trig_float, "
"uniform_01, zero, norm_dist, rand_int or uniform_low_precision, not "
<< hipblaslt_initialization2string(arg.initialization) << std::endl;
return;
#endif
}
if(arg.algo_method == 1)
{
#ifdef GOOGLE_TEST
GTEST_SKIP() << "MX data types do not support algorithm \"all\"";
#else
hipblaslt_cout << "MX data types do not support algorithm \"all\"" << std::endl;
return;
#endif
}
MXScaleLayout const scaleLayoutB
= mxScaleLayoutForFormat(arg.scaleB, mxProp.gcnArchName);
size_t dataBatchBytesB = (num_batches[i] > 1)
? elementsToBytes(stride_b[i], TiB)
: 0;
size_t scaleBatchBytesB = (num_batches[i] > 1) ? size_scaleBVec[i] : 0;
std::vector<float> refBAll;
refBAll.reserve(static_cast<size_t>(B_row[i]) * B_col[i] * num_batches[i]);
for(int64_t b = 0; b < num_batches[i]; b++)
{
auto* dataPtrB = reinterpret_cast<uint8_t*>(hB[i].buf()) + b * dataBatchBytesB;
auto* scalePtrB = reinterpret_cast<uint8_t*>(hScaleB[i].buf()) + b * scaleBatchBytesB;
auto batchRef
= generateMXInput(TiB,
scaleDataType(arg.scaleB),
dataPtrB,
scalePtrB,
B_row[i],
B_col[i],
ldb[i],
transB == HIPBLAS_OP_T,
1,
blockSize(arg.scaleB),
/*isMatrixA=*/false,
scaleLayoutB,
hipblaslt_initialization2string(arg.initialization),
/*min_val=*/-1.0f,
/*max_val=*/1.0f);
refBAll.insert(refBAll.end(), batchRef.begin(), batchRef.end());
}
refB.emplace_back(std::move(refBAll));
CHECK_HIP_ERROR(synchronize(dB[i], hB[i], block_count));
CHECK_HIP_ERROR(synchronize(dScaleB[i], hScaleB[i], block_count));
#else
#ifdef GOOGLE_TEST
GTEST_SKIP() << "MX data initialization requires HIPBLASLT_ENABLE_MXDATAGENERATOR=ON at build time";
#else
hipblaslt_cout << "MX data initialization requires HIPBLASLT_ENABLE_MXDATAGENERATOR=ON at build time"
<< std::endl;
return;
#endif
#endif
}
else
{
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
hipblaslt_init_device(ABC_dims::B,
arg.initialization,
alpha_isnan_type(arg, Talpha),
dB[i].buf(),
B_row[i],
B_col[i],
(do_swizzle_b) ? B_row[i] : ldb[i],
TiB,
(do_swizzle_b && stride_b[i] != 0) ? B_row[i] * B_col[i]
: stride_b[i],
num_batches[i]);
}
else
{
for(int batchCount = 0; batchCount < num_batches[i]; batchCount++)
{
hipblaslt_init_device(ABC_dims::B,
arg.initialization,
alpha_isnan_type(arg, Talpha),
dB[batchCount].buf(),
B_row[i],
B_col[i],
(do_swizzle_b) ? B_row[i] : ldb[i],
TiB,
(do_swizzle_b && stride_b[i] != 0) ? B_row[i] * B_col[i]
: stride_b[i],
1);
}
}
}
if(batchMode == HIPBLASLT_BATCH_MODE_STRIDED)
{
hipblaslt_init_device(ABC_dims::C,
arg.initialization,
beta_isnan_type(arg, Talpha),
dC[i].buf(),
M[i],
N[i],
ldc[i],
To,
stride_c[i],
num_batches[i]);
// generateMXInput already produced the reference floats and the
// kernel-ready scale layout for both A and B; nothing to do here.
// broadcast first block
CHECK_HIP_ERROR(broadcast(dA[i], block_count));
CHECK_HIP_ERROR(broadcast(dB[i], block_count));
CHECK_HIP_ERROR(broadcast(dC[i], block_count));
if(arg.unit_check || arg.norm_check || arg.allclose_check || do_swizzle_a
|| do_swizzle_b)
{
CHECK_HIP_ERROR(synchronize(hA[i],
dA[i],
num_batches[i],
A_row[i],
A_col[i],
lda[i],
realDataTypeSize(TiA),
do_swizzle_a,
stream));
// B is always stored as K×N in memory; use (K, N, ldb) not (B_row, B_col) to avoid row > lda when transB=T
CHECK_HIP_ERROR(synchronize(hB[i],
dB[i],
num_batches[i],
K[i],
N[i],
ldb[i],
realDataTypeSize(TiB),
do_swizzle_b,
stream));
CHECK_HIP_ERROR(synchronize(hC[i], dC[i], 0, 0, 0, 0, 1, false, stream));
if(arg.dump_matrix)
{
for(int batchId = 0; batchId < num_batches[i]; batchId++){
hipblasltDispatchValuesToFile(transA,
TiA,
M[i],
K[i],
lda[i],
hA[i].buf(),
"batch_" + std::to_string(batchId) + "_" + std::to_string(i) + "_A_input.txt");
hipblasltDispatchValuesToFile(transB,
TiB,
K[i],
N[i],
ldb[i],
hB[i].buf(),
"batch_" + std::to_string(batchId) + "_" + std::to_string(i) + "_B_input.txt");
hipblasltDispatchValuesToFile(HIPBLAS_OP_N,
To,
M[i],
N[i],
ldc[i],
hC[i].buf(),
"batch_" + std::to_string(batchId) + "_" + std::to_string(i) + "_C_input.txt");
}
}
}
if(do_swizzle_a)
{
HipHostBuffer tmp(TiA, size_dA[i]);
swizzle_tensor_type(
tmp, hA[i], TiA, arg, num_batches[i], M[i], K[i], lda[i], false);
CHECK_HIP_ERROR(synchronize(dA[i], tmp, block_count));
}
if(do_swizzle_b)
{
HipHostBuffer tmp(TiB, size_dB[i]);
swizzle_tensor_type(
tmp, hB[i], TiB, arg, num_batches[i], N[i], K[i], ldb[i], false);
CHECK_HIP_ERROR(synchronize(dB[i], tmp, block_count));
}
if(arg.gradient && arg.use_e)
{
hipblaslt_init(hE[i].buf(), M[i], N[i], lde[i], Taux, stride_e[i], num_batches[i]);
}
if(arg.bias_vector)
{
// Filling up unique bias values for each batch in Strided Batch
if(arg.bias_stride > 0)
hipblaslt_init(hBias[i].buf(), arg.bias_stride, 1, arg.bias_stride, Tbias, arg.bias_stride, num_batches[i]);
else
hipblaslt_init(hBias[i].buf(), size_bias[i], 1, size_bias[i], Tbias);
}
if(arg.scaleA == hipblaslt_scaling_format::Scalar
|| arg.scaleA == hipblaslt_scaling_format::Vector)
{
if(arg.norm_check)
hipblaslt_init_small(
hScaleA[i].buf(), size_scaleAVec[i], 1, size_scaleAVec[i], Talpha);
else
hipblaslt_init(
hScaleA[i].buf(), size_scaleAVec[i], 1, size_scaleAVec[i], Talpha);
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar
|| arg.scaleB == hipblaslt_scaling_format::Vector)
{
if(arg.norm_check)
hipblaslt_init_small(
hScaleB[i].buf(), size_scaleBVec[i], 1, size_scaleBVec[i], Talpha);
else
hipblaslt_init(
hScaleB[i].buf(), size_scaleBVec[i], 1, size_scaleBVec[i], Talpha);
}
if(arg.scaleC)
{
if(To == HIP_R_8F_E4M3_FNUZ || To == HIP_R_8F_E5M2_FNUZ)
{
hipblaslt_init_small(hScaleC[i].buf(), 1, 1, 1, Talpha);
}
else
{
hipblaslt_init(hScaleC[i].buf(), 1, 1, 1, Talpha);
}
}
if(arg.scaleD)
{
if(To == HIP_R_8F_E4M3_FNUZ || To == HIP_R_8F_E5M2_FNUZ)
{
hipblaslt_init_small(hScaleD[i].buf(), 1, 1, 1, Talpha);
}
else
{
hipblaslt_init(hScaleD[i].buf(), 1, 1, 1, Talpha);
}
}
if(arg.amaxD)
hipblaslt_init_zero(hAmaxD_gold[i].buf(), 1, 1, 1, Talpha);
if(arg.scaleE)
hipblaslt_init(hScaleE[i].buf(), 1, 1, 1, Talpha);
if(arg.scaleAlpha_vector)
hipblaslt_init(hScaleAlphaVec[i].buf(), M[i], 1, M[i], Talpha);
if(arg.gradient && arg.use_e)
{
CHECK_HIP_ERROR(synchronize(dE[i], hE[i], block_count));
}
if(!arg.gradient && arg.bias_vector)
{
CHECK_HIP_ERROR(synchronize(dBias[i], hBias[i], block_count));
}
if(arg.scaleAlpha_vector)
{
CHECK_HIP_ERROR(synchronize(dScaleAlphaVec[i], hScaleAlphaVec[i], block_count));
alpha_in[i] = dScaleAlphaVec[i].buf();
set_computeInterface(
h_alpha[i],
1.0,
Tc,
TiA); // use dScaleAlphaVec instead, original alpha = 1.0 for verify
}
else
alpha_in[i] = &(h_alpha[i]);
if(arg.scaleA == hipblaslt_scaling_format::Scalar
|| arg.scaleA == hipblaslt_scaling_format::Vector)
{
if(arg.amaxScaleA && (arg.a_type == HIP_R_32F || arg.a_type == HIP_R_16F))
{
CHECK_HIPBLASLT_ERROR(hipblasltExtAMax(arg.a_type,
HIP_R_32F,
dScaleA[i].buf(),
dA[i].buf(),
A_row[i],
A_col[i],
stream));
CHECK_HIP_ERROR(synchronize(hScaleA[i], dScaleA[i]));
}
else
CHECK_HIP_ERROR(synchronize(dScaleA[i], hScaleA[i], block_count));
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar
|| arg.scaleB == hipblaslt_scaling_format::Vector)
{
if(arg.amaxScaleB && (arg.b_type == HIP_R_32F || arg.b_type == HIP_R_16F))
{
CHECK_HIPBLASLT_ERROR(hipblasltExtAMax(arg.b_type,
HIP_R_32F,
dScaleB[i].buf(),
dB[i].buf(),
B_row[i],
B_col[i],
stream));
CHECK_HIP_ERROR(synchronize(hScaleB[i], dScaleB[i]));
}
else
CHECK_HIP_ERROR(synchronize(dScaleB[i], hScaleB[i], block_count));
}
if(arg.scaleC)
CHECK_HIP_ERROR(synchronize(dScaleC[i], hScaleC[i]));
if(arg.scaleD)
CHECK_HIP_ERROR(synchronize(dScaleD[i], hScaleD[i]));
if(arg.scaleE)
CHECK_HIP_ERROR(synchronize(dScaleE[i], hScaleE[i]));
//// copy data from CPU to device end
if(size_D_copy[i])
{
if(epilogue_on[i])
{
transform_buf(hC[i], hD_gold_epl[i], To, Talpha);
}
else
{
copy_buf(hC[i], hD_gold[i], To);
}
}
if(epilogue_on[i])
{
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE,
&(epilogue[i]),
sizeof(epilogue[i])),
HIPBLAS_STATUS_SUCCESS);
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE_ACT_ARG0_EXT,
&(act0[i]),
sizeof(act0[i])));
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE_ACT_ARG1_EXT,
&(act1[i]),
sizeof(act1[i])));
}
if(arg.use_e)
{
void* e_addr = dE[i].buf();
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_POINTER,
&e_addr,
sizeof(void*)));
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_DATA_TYPE,
&arg.aux_type,
sizeof(hipDataType)));
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_LD, &lde[i], sizeof(int64_t)));
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_BATCH_STRIDE,
&stride_e[i],
sizeof(int64_t)));
}
if(arg.bias_vector)
{
const void* bias_addr;
int32_t bias_stride = arg.bias_stride;
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_BIAS_DATA_TYPE,
&arg.bias_type,
sizeof(hipDataType)),
HIPBLAS_STATUS_SUCCESS);
bias_addr = dBias[i].buf();
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_BIAS_POINTER,
&bias_addr,
sizeof(void*)),
HIPBLAS_STATUS_SUCCESS);
if(bias_stride > 0)
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_BIAS_BATCH_STRIDE,
&bias_stride,
sizeof(bias_stride)),
HIPBLAS_STATUS_SUCCESS);
}
if(arg.scaleA != hipblaslt_scaling_format::none)
{
hipblasLtMatmulDescAttributes_t attr = HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER;
void* scaleA_addr = (void*)(dScaleA[i].buf());
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], attr, &scaleA_addr, sizeof(void*)));
hipblasLtMatmulMatrixScale_t mode = HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;
if(arg.scaleA == hipblaslt_scaling_format::Vector)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_OUTER_VEC_32F;
}
// For MX format (SCALE_POINTER_BLOCK), set the scale mode
// Set the row and col sizes of scale block for matrix A
else if(arg.scaleA == hipblaslt_scaling_format::Block_32_UE8M0)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;
}
else if(arg.scaleA == hipblaslt_scaling_format::Block_16_UE8M0)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE8M0_EXT;
}
else if(arg.scaleA == hipblaslt_scaling_format::Block_32_UE4M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE4M3_EXT;
}
else if(arg.scaleA == hipblaslt_scaling_format::Block_16_UE4M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;
}
else if(arg.scaleA == hipblaslt_scaling_format::Block_32_UE5M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE5M3_EXT;
}
else if(arg.scaleA == hipblaslt_scaling_format::Block_16_UE5M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE5M3_EXT;
}
else if(arg.scaleA == hipblaslt_scaling_format::Block_32_UE8M0_32_8_EXT)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_BLK32_UE8M0_32_8_EXT;
}
if(mode != HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F)
{
auto attr = HIPBLASLT_MATMUL_DESC_A_SCALE_MODE;
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], attr, &mode, sizeof(uint32_t)));
}
}
if(arg.scaleB != hipblaslt_scaling_format::none)
{
hipblasLtMatmulDescAttributes_t attr = HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER;
void* scaleB_addr = (void*)(dScaleB[i].buf());
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], attr, &scaleB_addr, sizeof(void*)));
hipblasLtMatmulMatrixScale_t mode = HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;
if(arg.scaleB == hipblaslt_scaling_format::Vector)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_OUTER_VEC_32F;
}
// For MX format (SCALE_POINTER_BLOCK), set the scale mode
// Set the row and col sizes of scale block for matrix B
else if(arg.scaleB == hipblaslt_scaling_format::Block_32_UE8M0)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;
}
else if(arg.scaleB == hipblaslt_scaling_format::Block_16_UE8M0)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE8M0_EXT;
}
else if(arg.scaleB == hipblaslt_scaling_format::Block_32_UE4M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE4M3_EXT;
}
else if(arg.scaleB == hipblaslt_scaling_format::Block_16_UE4M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;
}
else if(arg.scaleB == hipblaslt_scaling_format::Block_32_UE5M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE5M3_EXT;
}
else if(arg.scaleB == hipblaslt_scaling_format::Block_16_UE5M3)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE5M3_EXT;
}
else if(arg.scaleB == hipblaslt_scaling_format::Block_32_UE8M0_32_8_EXT)
{
mode = HIPBLASLT_MATMUL_MATRIX_SCALE_BLK32_UE8M0_32_8_EXT;
}
if(mode != HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F)
{
auto attr = HIPBLASLT_MATMUL_DESC_B_SCALE_MODE;
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], attr, &mode, sizeof(uint32_t)));
}
}
if(arg.scaleC)
{
void* scaleC_addr = dScaleC[i].buf();
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_C_SCALE_POINTER,
&scaleC_addr,
sizeof(void*)));
}
if(arg.scaleD)
{
void* scaleD_addr = dScaleD[i].buf();
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_D_SCALE_POINTER,
&scaleD_addr,
sizeof(void*)));
}
if(arg.amaxD)
{
void* amaxD_addr = dAmaxD[i].buf();
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_AMAX_D_POINTER,
&amaxD_addr,
sizeof(void*)));
}
if(arg.scaleE)
{
void* scaleE_addr = dScaleE[i].buf();
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_SCALE_POINTER,
&scaleE_addr,
sizeof(void*)));
}
if(arg.scaleAlpha_vector)
{
hipblasLtPointerMode_t scale_mode
= HIPBLASLT_POINTER_MODE_ALPHA_DEVICE_VECTOR_BETA_HOST;
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_POINTER_MODE,
&scale_mode,
sizeof(scale_mode)),
HIPBLAS_STATUS_SUCCESS);
}
}
else
{
alpha_in[i] = &(h_alpha[i]);
for(int batchCount = 0; batchCount < num_batches[i]; batchCount++)
{
hipblaslt_init_device(ABC_dims::C,
arg.initialization,
beta_isnan_type(arg, Talpha),
dC[batchCount].buf(),
M[i],
N[i],
ldc[i],
To,
stride_c[i],
1);
// broadcast first block
CHECK_HIP_ERROR(broadcast(dA[batchCount], block_count));
CHECK_HIP_ERROR(broadcast(dB[batchCount], block_count));
CHECK_HIP_ERROR(broadcast(dC[batchCount], block_count));
if(arg.unit_check || arg.norm_check || arg.allclose_check || do_swizzle_a
|| do_swizzle_b)
{
CHECK_HIP_ERROR(synchronize(hA[batchCount],
dA[batchCount],
1,
A_row[i],
A_col[i],
lda[i],
realDataTypeSize(TiA),
do_swizzle_a));
CHECK_HIP_ERROR(synchronize(hB[batchCount],
dB[batchCount],
1,
B_row[i],
B_col[i],
ldb[i],
realDataTypeSize(TiB),
do_swizzle_b));
CHECK_HIP_ERROR(synchronize(hC[batchCount], dC[batchCount]));
}
if(arg.dump_matrix)
{
hipblasltDispatchValuesToFile(transA,
TiA,
M[i],
K[i],
lda[i],
hA[batchCount].buf(),
"batch_" + std::to_string(i) + "_A_"
+ std::to_string(batchCount) + "_input.txt");
hipblasltDispatchValuesToFile(transB,
TiB,
K[i],
N[i],
ldb[i],
hB[batchCount].buf(),
"batch_" + std::to_string(i) + "_B_"
+ std::to_string(batchCount) + "_input.txt");
hipblasltDispatchValuesToFile(HIPBLAS_OP_N,
To,
M[i],
N[i],
ldc[i],
hC[batchCount].buf(),
"batch_" + std::to_string(i) + "_C_"
+ std::to_string(batchCount) + "_input.txt");
}
if(do_swizzle_a)
{
HipHostBuffer tmp(TiA, size_dA[i]);
swizzle_tensor_type(
tmp, hA[batchCount], TiA, arg, 1, M[i], K[i], lda[i], false);
CHECK_HIP_ERROR(synchronize(dA[batchCount], tmp, block_count));
}
if(do_swizzle_b)
{
HipHostBuffer tmp(TiB, size_dB[i]);
swizzle_tensor_type(
tmp, hB[batchCount], TiB, arg, 1, N[i], K[i], ldb[i], false);
CHECK_HIP_ERROR(synchronize(dB[batchCount], tmp, block_count));
}
//// copy data from CPU to device end
if(size_D_copy[i])
{
copy_buf(hC[batchCount], hD_gold[batchCount], To);
}
}
if(arg.scaleA == hipblaslt_scaling_format::Scalar)
{
if(arg.norm_check)
hipblaslt_init_small(
hScaleA[i].buf(), size_scaleAVec[i], 1, size_scaleAVec[i], Talpha);
else
hipblaslt_init(
hScaleA[i].buf(), size_scaleAVec[i], 1, size_scaleAVec[i], Talpha);
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar)
{
if(arg.norm_check)
hipblaslt_init_small(
hScaleB[i].buf(), size_scaleBVec[i], 1, size_scaleBVec[i], Talpha);
else
hipblaslt_init(
hScaleB[i].buf(), size_scaleBVec[i], 1, size_scaleBVec[i], Talpha);
}
if(arg.scaleC)
{
if(To == HIP_R_8F_E4M3_FNUZ || To == HIP_R_8F_E5M2_FNUZ)
{
hipblaslt_init_small(hScaleC[i].buf(), 1, 1, 1, Talpha);
}
else
{
hipblaslt_init(hScaleC[i].buf(), 1, 1, 1, Talpha);
}
}
if(arg.scaleD)
{
if(To == HIP_R_8F_E4M3_FNUZ || To == HIP_R_8F_E5M2_FNUZ)
{
hipblaslt_init_small(hScaleD[i].buf(), 1, 1, 1, Talpha);
}
else
{
hipblaslt_init(hScaleD[i].buf(), 1, 1, 1, Talpha);
}
}
if(arg.scaleA == hipblaslt_scaling_format::Scalar)
{
if(arg.amaxScaleA && (arg.a_type == HIP_R_32F || arg.a_type == HIP_R_16F))
{
CHECK_HIPBLASLT_ERROR(hipblasltExtAMax(arg.a_type,
HIP_R_32F,
dScaleA[i].buf(),
dA[i].buf(),
A_row[i],
A_col[i],
stream));
CHECK_HIP_ERROR(synchronize(hScaleA[i], dScaleA[i]));
}
else
CHECK_HIP_ERROR(synchronize(dScaleA[i], hScaleA[i], block_count));
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar)
{
if(arg.amaxScaleB && (arg.b_type == HIP_R_32F || arg.b_type == HIP_R_16F))
{
CHECK_HIPBLASLT_ERROR(hipblasltExtAMax(arg.b_type,
HIP_R_32F,
dScaleB[i].buf(),
dB[i].buf(),
B_row[i],
B_col[i],
stream));
CHECK_HIP_ERROR(synchronize(hScaleB[i], dScaleB[i]));
}
else
CHECK_HIP_ERROR(synchronize(dScaleB[i], hScaleB[i], block_count));
}
if(arg.scaleC)
CHECK_HIP_ERROR(synchronize(dScaleC[i], hScaleC[i]));
if(arg.scaleD)
CHECK_HIP_ERROR(synchronize(dScaleD[i], hScaleD[i]));
if(arg.scaleA == hipblaslt_scaling_format::Scalar)
{
hipblasLtMatmulDescAttributes_t attr = HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER;
void* scaleA_addr = (void*)(dScaleA[i].buf());
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], attr, &scaleA_addr, sizeof(void*)));
hipblasLtMatmulMatrixScale_t mode = HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;
attr = HIPBLASLT_MATMUL_DESC_A_SCALE_MODE;
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i], attr, &mode, sizeof(uint32_t)));
}
if(arg.scaleB == hipblaslt_scaling_format::Scalar)
{
hipblasLtMatmulDescAttributes_t attr = HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER;
void* scaleB_addr = (void*)(dScaleB[i].buf());
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[0][i], attr, &scaleB_addr, sizeof(void*)));
hipblasLtMatmulMatrixScale_t mode = HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;
attr = HIPBLASLT_MATMUL_DESC_B_SCALE_MODE;
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i], attr, &mode, sizeof(uint32_t)));
}
if(arg.scaleC)
{
void* scaleC_addr = dScaleC[i].buf();
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_C_SCALE_POINTER,
&scaleC_addr,
sizeof(void*)));
}
if(arg.scaleD)
{
void* scaleD_addr = dScaleD[i].buf();
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[0][i],
HIPBLASLT_MATMUL_DESC_D_SCALE_POINTER,
&scaleD_addr,
sizeof(void*)));
}
}
for(int32_t b = 1; b < matmul.size(); b++)
{
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescCreate(&(matmul[b][i]), arg.compute_type, arg.scale_type));
CHECK_HIPBLASLT_ERROR(hipblaslt_ext::copyMatmul(matmul[0][i], matmul[b][i]));
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[b][i],
HIPBLASLT_MATMUL_DESC_COMPUTE_INPUT_TYPE_A_EXT,
&TciA,
sizeof(void*)),
HIPBLAS_STATUS_SUCCESS);
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[b][i],
HIPBLASLT_MATMUL_DESC_COMPUTE_INPUT_TYPE_B_EXT,
&TciB,
sizeof(void*)),
HIPBLAS_STATUS_SUCCESS);
// Forward CLI knobs from hipblaslt-bench into the matmul descriptor.
{
int32_t sm = hipblaslt_bench_options::sm_count_target();
if(sm != 0)
{
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[b][i],
HIPBLASLT_MATMUL_DESC_SM_COUNT_TARGET,
&sm,
sizeof(sm)));
}
int32_t dyn = hipblaslt_bench_options::streamk_tile_scheduling_mode();
if(dyn >= 0)
{
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[b][i],
HIPBLASLT_MATMUL_DESC_STREAMK_TILE_SCHEDULING_EXT,
&dyn,
sizeof(dyn)));
}
}
if(batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
// Update bias, E
if(arg.bias_vector)
{
const void* bias_addr
= (const void*)(dBias[i].as<char>()
+ b * size_bias[i] * realDataTypeSize(Tbias));
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulDescSetAttribute(matmul[b][i],
HIPBLASLT_MATMUL_DESC_BIAS_POINTER,
&bias_addr,
sizeof(void*)),
HIPBLAS_STATUS_SUCCESS);
}
if(arg.use_e)
{
void* e_addr
= (void*)(dE[i].as<char>() + b * size_E[i] * realDataTypeSize(Taux));
CHECK_HIPBLASLT_ERROR(
hipblasLtMatmulDescSetAttribute(matmul[b][i],
HIPBLASLT_MATMUL_DESC_EPILOGUE_AUX_POINTER,
&e_addr,
sizeof(void*)));
}
}
if(arg.scaleA != hipblaslt_scaling_format::none)
{
hipblasLtMatmulDescAttributes_t attr = HIPBLASLT_MATMUL_DESC_A_SCALE_POINTER;
void* scaleA_addr = (void*)(dScaleA[i].as<char>() + b * size_scaleAVec[i]);
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[b][i], attr, &scaleA_addr, sizeof(void*)));
}
if(arg.scaleB != hipblaslt_scaling_format::none)
{
hipblasLtMatmulDescAttributes_t attr = HIPBLASLT_MATMUL_DESC_B_SCALE_POINTER;
void* scaleB_addr = (void*)(dScaleB[i].as<char>() + b * size_scaleBVec[i]);
CHECK_HIPBLASLT_ERROR(hipblasLtMatmulDescSetAttribute(
matmul[b][i], attr, &scaleB_addr, sizeof(void*)));
}
}
}
// set preference
size_t max_workspace_size = arg.user_allocated_workspace;
hipblaslt_local_preference pref;
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmulPreferenceSetAttribute(pref,
HIPBLASLT_MATMUL_PREF_MAX_WORKSPACE_BYTES,
&max_workspace_size,
sizeof(max_workspace_size)),
HIPBLAS_STATUS_SUCCESS);
// set workspace
device_vector<unsigned char>* dWorkspace = nullptr;
size_t workspace_size = 0;
// set user args
hipblaslt_ext::UserArguments* userArgs = nullptr;
hipblaslt_ext::UserArguments* d_userArgs = nullptr;
// Get Heuristic results
int32_t requestAlgoCount = arg.requested_solution_num < 0 ? HIPBLASLT_MAX_REQUESTED_SOLUTION_NUM
: arg.requested_solution_num;
int returnedAlgoCount = 0;
std::vector<hipblasLtMatmulHeuristicResult_t> heuristicResult;
std::vector<size_t> heuristicTuningIndex;
// Cpp API
hipblaslt_ext::GemmPreference gemmPref;
gemmPref.setMaxWorkspaceBytes(max_workspace_size);
std::vector<hipblaslt_ext::Gemm> gemmVec;
std::vector<hipblaslt_ext::GroupedGemm> groupedGemmVec;
std::vector<std::vector<hipblaslt_ext::GemmInputs>> extinputs;
//Updating the gemm_count for the below section of the code
// as batch_count for reusing existing GroupedGEMM code for General Batched GEMM
batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY ? gemm_count = arg.batch_count : gemm_count;
// C to Cpp API for GG
std::vector<std::vector<void*>> da(block_count, std::vector<void*>(gemm_count));
std::vector<std::vector<void*>> db(block_count, std::vector<void*>(gemm_count));
std::vector<std::vector<void*>> dc(block_count, std::vector<void*>(gemm_count));
std::vector<std::vector<void*>> dd(block_count, std::vector<void*>(gemm_count));
std::vector<std::vector<uint64_t*>> da1(block_count, std::vector<uint64_t*>(gemm_count));
std::vector<std::vector<uint64_t*>> db1(block_count, std::vector<uint64_t*>(gemm_count));
std::vector<std::vector<uint64_t*>> dc1(block_count, std::vector<uint64_t*>(gemm_count));
std::vector<std::vector<uint64_t*>> dd1(block_count, std::vector<uint64_t*>(gemm_count));
std::vector<uint64_t*> dda, ddb, ddc, ddd;
std::vector<uint64_t*> hha, hhb, hhc, hhd;
for(int i = 0; i < block_count; i++)
{
uint64_t* ptr = nullptr;
CHECK_HIP_ERROR(hipMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
dda.push_back(ptr);
CHECK_HIP_ERROR(hipHostMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
hha.push_back(ptr);
CHECK_HIP_ERROR(hipMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
ddb.push_back(ptr);
CHECK_HIP_ERROR(hipHostMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
hhb.push_back(ptr);
CHECK_HIP_ERROR(hipMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
ddc.push_back(ptr);
CHECK_HIP_ERROR(hipHostMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
hhc.push_back(ptr);
CHECK_HIP_ERROR(hipMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
ddd.push_back(ptr);
CHECK_HIP_ERROR(hipHostMalloc(&ptr, gemm_count * sizeof(uint64_t*)));
hhd.push_back(ptr);
}
for(int32_t b = 0; b < block_count; b++)
{
if(!do_grouped_gemm)
gemmVec.push_back(hipblaslt_ext::Gemm(handle,
transA,
transB,
arg.a_type,
arg.b_type,
arg.c_type,
arg.d_type,
arg.compute_type));
else
groupedGemmVec.push_back(hipblaslt_ext::GroupedGemm(handle,
transA,
transB,
arg.a_type,
arg.b_type,
arg.c_type,
arg.d_type,
arg.compute_type));
}
std::vector<hipblaslt_ext::GemmEpilogue> extepilogue;
hipblaslt_ext::GemmProblemType extproblemtype;
if(arg.use_ext_setproblem && batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
extinputs.resize(block_count, std::vector<hipblaslt_ext::GemmInputs>(gemm_count));
extepilogue.resize(gemm_count);
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
auto bias_type = HIPBLASLT_DATATYPE_INVALID;
auto aux_type = HIPBLASLT_DATATYPE_INVALID;
void* bias_addr = nullptr;
for(int32_t b = 0; b < block_count; b++)
{
if(arg.bias_vector)
{
bias_type = arg.bias_type;
bias_addr = (void*)(dBias[gemmIdx].as<char>()
+ b * size_bias[gemmIdx] * realDataTypeSize(bias_type));
}
if(arg.use_e)
{
aux_type = arg.aux_type;
}
if(b == 0)
{
auto toMatrixScale = [](hipblaslt_scaling_format f) {
switch(f)
{
case hipblaslt_scaling_format::Vector:
return HIPBLASLT_MATMUL_MATRIX_SCALE_OUTER_VEC_32F;
case hipblaslt_scaling_format::Block_32_UE8M0:
return HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0;
case hipblaslt_scaling_format::Block_16_UE8M0:
return HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE8M0_EXT;
case hipblaslt_scaling_format::Block_32_UE4M3:
return HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE4M3_EXT;
case hipblaslt_scaling_format::Block_16_UE4M3:
return HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3;
case hipblaslt_scaling_format::Block_32_UE5M3:
return HIPBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE5M3_EXT;
case hipblaslt_scaling_format::Block_16_UE5M3:
return HIPBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE5M3_EXT;
case hipblaslt_scaling_format::Block_32_UE8M0_32_8_EXT:
return HIPBLASLT_MATMUL_MATRIX_SCALE_BLK32_UE8M0_32_8_EXT;
default:
return HIPBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F;
}
};
extepilogue[gemmIdx].setMode(epilogue[gemmIdx]);
extepilogue[gemmIdx].setBiasDataType(bias_type);
extepilogue[gemmIdx].setAuxDataType(aux_type);
extepilogue[gemmIdx].setAuxLeadingDimension(lde[gemmIdx]);
extepilogue[gemmIdx].setAuxBatchStride(stride_e[gemmIdx]);
extepilogue[gemmIdx].setScalingAType(toMatrixScale(arg.scaleA));
extepilogue[gemmIdx].setScalingBType(toMatrixScale(arg.scaleB));
}
extinputs[b][gemmIdx].setA((void*)((dA[gemmIdx].as<char>())
+ b * size_dA[gemmIdx] * realDataTypeSize(TiA)));
extinputs[b][gemmIdx].setB((void*)((dB[gemmIdx].as<char>())
+ b * size_dB[gemmIdx] * realDataTypeSize(TiB)));
extinputs[b][gemmIdx].setC(
(void*)((dC[gemmIdx].as<char>()) + b * size_C[gemmIdx] * realDataTypeSize(To)));
extinputs[b][gemmIdx].setD((void*)(((*dDp)[gemmIdx].as<char>())
+ b * size_D[gemmIdx] * realDataTypeSize(To)));
extinputs[b][gemmIdx].setAlpha(&h_alpha[gemmIdx]);
extinputs[b][gemmIdx].setBeta(&h_beta[gemmIdx]);
extinputs[b][gemmIdx].setBias(bias_addr);
extinputs[b][gemmIdx].setScaleA(
arg.scaleA != hipblaslt_scaling_format::none
? (void*)((dScaleA[gemmIdx].as<char>()) + b * size_scaleAVec[gemmIdx])
: nullptr);
extinputs[b][gemmIdx].setScaleB(
arg.scaleB != hipblaslt_scaling_format::none
? (void*)((dScaleB[gemmIdx].as<char>()) + b * size_scaleBVec[gemmIdx])
: nullptr);
extinputs[b][gemmIdx].setScaleC(arg.scaleC ? dScaleC[gemmIdx].as<char>() : nullptr);
extinputs[b][gemmIdx].setScaleD(arg.scaleD ? dScaleD[gemmIdx].as<char>() : nullptr);
extinputs[b][gemmIdx].setScaleAux(arg.scaleE ? dScaleE[gemmIdx].as<char>()
: nullptr);
extinputs[b][gemmIdx].setAmaxD(arg.amaxD ? dAmaxD[gemmIdx].as<char>() : nullptr);
if(arg.use_e)
extinputs[b][gemmIdx].setAux(
(void*)((dE[gemmIdx].as<char>())
+ b * size_E[gemmIdx] * realDataTypeSize(Taux)));
if(arg.scaleAlpha_vector)
extinputs[b][gemmIdx].setScaleAlphaVec(
(void*)((dScaleAlphaVec[gemmIdx].as<char>())
+ b * size_scaleAlphaVec[gemmIdx] * realDataTypeSize(Talpha)));
}
}
extproblemtype.setOpA(transA);
extproblemtype.setOpB(transB);
extproblemtype.setTypeA(arg.a_type);
extproblemtype.setTypeB(arg.b_type);
extproblemtype.setTypeC(arg.c_type);
extproblemtype.setTypeD(arg.d_type);
extproblemtype.setTypeCompute(arg.compute_type);
if(do_swizzle_a)
{
hipblasLtOrder_t orderA = orderForDatatype(TiA);
extproblemtype.setOrderA(orderA);
}
if(do_swizzle_b)
{
hipblasLtOrder_t orderB = orderForDatatype(TiB);
extproblemtype.setOrderB(orderB);
}
}
else if(arg.grouped_gemm)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
for(int32_t b = 0; b < block_count; b++)
{
da[b][gemmIdx] = (void*)((dA[gemmIdx].as<char>())
+ b * size_dA[gemmIdx] * realDataTypeSize(TiA));
db[b][gemmIdx] = (void*)((dB[gemmIdx].as<char>())
+ b * size_dB[gemmIdx] * realDataTypeSize(TiB));
dc[b][gemmIdx] = (void*)((dC[gemmIdx].as<char>())
+ b * size_C[gemmIdx] * realDataTypeSize(To));
dd[b][gemmIdx] = (void*)(((*dDp)[gemmIdx].as<char>())
+ b * size_D[gemmIdx] * realDataTypeSize(To));
}
}
}
else
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
for(int32_t b = 0; b < block_count; b++)
{
da1[b][gemmIdx] = reinterpret_cast<uint64_t*>(
(dA[gemmIdx].as<char>()) + b * size_dA[0] * realDataTypeSize(TiA));
db1[b][gemmIdx] = reinterpret_cast<uint64_t*>(
(dB[gemmIdx].as<char>()) + b * size_dB[0] * realDataTypeSize(TiB));
dc1[b][gemmIdx] = reinterpret_cast<uint64_t*>(
(dC[gemmIdx].as<char>()) + b * size_C[0] * realDataTypeSize(To));
dd1[b][gemmIdx] = reinterpret_cast<uint64_t*>(
(*dDp)[gemmIdx].as<char>() + b * size_D[0] * realDataTypeSize(To));
}
}
}
if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
//Copy The pointer arrays to Device [General Batched GEMM]
for(int32_t b = 0; b < block_count; b++)
{
CHECK_HIP_ERROR(hipMemcpy(
dda[b], da1[b].data(), gemm_count * sizeof(uint64_t*), hipMemcpyHostToDevice));
CHECK_HIP_ERROR(hipMemcpy(
ddb[b], db1[b].data(), gemm_count * sizeof(uint64_t*), hipMemcpyHostToDevice));
CHECK_HIP_ERROR(hipMemcpy(
ddc[b], dc1[b].data(), gemm_count * sizeof(uint64_t*), hipMemcpyHostToDevice));
CHECK_HIP_ERROR(hipMemcpy(
ddd[b], dd1[b].data(), gemm_count * sizeof(uint64_t*), hipMemcpyHostToDevice));
}
}
hipblaslt_ext::GemmType gemmType = do_grouped_gemm
? hipblaslt_ext::GemmType::HIPBLASLT_GROUPED_GEMM
: hipblaslt_ext::GemmType::HIPBLASLT_GEMM;
// Remove duplicate
std::vector<uint32_t> gsu_vector;
std::vector<uint32_t> wgm_vector;
for(int32_t i = 0; i < MAX_SUPPORTED_NUM_PROBLEMS; i++)
{
if(arg.gsu_vector[i] == -1)
break;
gsu_vector.push_back(arg.gsu_vector[i]);
}
for(int32_t i = 0; i < MAX_SUPPORTED_NUM_PROBLEMS; i++)
{
if(arg.wgm_vector[i] == -1)
break;
wgm_vector.push_back(arg.wgm_vector[i]);
}
std::set<uint32_t> remove_duplicate(gsu_vector.begin(), gsu_vector.end());
gsu_vector.assign(remove_duplicate.begin(), remove_duplicate.end());
remove_duplicate = std::set<uint32_t>(wgm_vector.begin(), wgm_vector.end());
wgm_vector.assign(remove_duplicate.begin(), remove_duplicate.end());
std::vector<hipblaslt_ext::GemmTuning> tuningVec;
if(arg.use_ext)
{
for(size_t wgm = 0; wgm < wgm_vector.size(); wgm++)
for(size_t gsu = 0; gsu < gsu_vector.size(); gsu++)
{
hipblaslt_ext::GemmTuning tuning;
tuning.setSplitK(gsu_vector[gsu]);
tuning.setWgm(wgm_vector[wgm]);
tuningVec.push_back(tuning);
}
}
else
{
// C API does not support
tuningVec.push_back(hipblaslt_ext::GemmTuning());
}
if(arg.algo_method == 2)
{
heuristicResult.clear();
heuristicTuningIndex.clear();
const bool indicesAreDiscovered = (arg.solution_index == -1);
std::vector<int> validIndices;
auto discoverValidIndices = [&]() {
std::vector<hipblasLtMatmulHeuristicResult_t> allAlgos;
EXPECT_HIPBLAS_STATUS(hipblaslt_ext::getAllAlgos(handle,
gemmType,
transA,
transB,
arg.a_type,
arg.b_type,
arg.c_type,
arg.d_type,
arg.compute_type,
allAlgos),
HIPBLAS_STATUS_SUCCESS);
validIndices.reserve(allAlgos.size());
for(auto& a : allAlgos)
{
validIndices.push_back(hipblaslt_ext::getIndexFromAlgo(a.algo));
}
};
if(indicesAreDiscovered)
{
discoverValidIndices();
}
bool selectionWasAttempted = false;
auto searchForSupportedAlgoViaIndexAPI = [&]() {
constexpr size_t batchSize = 100;
size_t batchStart = 0;
bool explicitConsumed = false;
auto nextBatchOfIndices = [&]() -> std::optional<std::vector<int>> {
if(indicesAreDiscovered)
{
if(batchStart >= validIndices.size())
{
return std::nullopt;
}
const size_t batchEnd
= std::min<size_t>(batchStart + batchSize, validIndices.size());
std::vector<int> batch(validIndices.begin() + batchStart,
validIndices.begin() + batchEnd);
batchStart = batchEnd;
return batch;
}
if(explicitConsumed)
{
return std::nullopt;
}
explicitConsumed = true;
return std::vector<int>{arg.solution_index};
};
auto fetchAlgosForBatch
= [&](std::vector<int>& batch,
std::vector<hipblasLtMatmulHeuristicResult_t>& candidates) {
candidates.clear();
const auto status
= hipblaslt_ext::getAlgosFromIndex(handle, batch, candidates);
if(indicesAreDiscovered)
{
EXPECT_HIPBLAS_STATUS(status, HIPBLAS_STATUS_SUCCESS);
}
};
auto configureExtGemmForCurrentProblem = [&]() {
if(arg.use_ext_setproblem)
{
for(int32_t b = 0; b < block_count; b++)
{
CHECK_HIPBLASLT_ERROR(gemmVec[b].setProblem(M[0],
N[0],
K[0],
num_batches[0],
lda[0],
ldb[0],
ldc[0],
ldd[0],
stride_da[0],
stride_db[0],
stride_c[0],
stride_d[0],
extepilogue[0],
extinputs[b][0],
extproblemtype));
}
return;
}
for(int32_t b = 0; b < block_count; b++)
{
CHECK_HIPBLASLT_ERROR(gemmVec[b].setProblem(
matmul[b][0],
alpha_in[0],
(dA[0].as<char>()) + b * size_dA[0] * realDataTypeSize(TiA),
matA[0],
(dB[0].as<char>()) + b * size_dB[0] * realDataTypeSize(TiB),
matB[0],
&h_beta[0],
(dC[0].as<char>()) + b * size_C[0] * realDataTypeSize(To),
matC[0],
((*dDp)[0].as<char>()) + b * size_D[0] * realDataTypeSize(To),
matD[0]));
}
};
auto configureGroupedGemmForCurrentProblem = [&]() {
if(arg.use_ext_setproblem)
{
auto num_batches_64
= std::vector<int64_t>{num_batches.begin(), num_batches.end()};
for(int32_t b = 0; b < block_count; b++)
{
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].setProblem(M,
N,
K,
num_batches_64,
lda,
ldb,
ldc,
ldd,
stride_da,
stride_db,
stride_c,
stride_d,
extepilogue,
extinputs[b],
extproblemtype));
}
return;
}
std::vector<void*> h_alpha_void, h_beta_void;
for(size_t i = 0; i < h_alpha.size(); i++)
{
h_alpha_void.push_back(&h_alpha[i]);
h_beta_void.push_back(&h_beta[i]);
}
for(int32_t b = 0; b < block_count; b++)
{
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].setProblem(matmul[b],
h_alpha_void,
da[b],
matA,
db[b],
matB,
h_beta_void,
dc[b],
matC,
dd[b],
matD));
}
};
auto collectTuningsForFirstViableAlgo
= [&](std::vector<hipblasLtMatmulHeuristicResult_t>& candidates,
auto& gemmObject,
bool& foundAlgo) {
foundAlgo = false;
for(int j = 0; j < returnedAlgoCount; j++)
{
for(size_t t = 0; t < tuningVec.size(); t++)
{
size_t tmpWorkspaceSize = 0;
if(gemmObject.isAlgoSupported(
candidates[j].algo, tuningVec[t], tmpWorkspaceSize)
!= HIPBLAS_STATUS_SUCCESS)
{
continue;
}
if(tmpWorkspaceSize > max_workspace_size)
{
continue;
}
heuristicResult.push_back(candidates[j]);
heuristicTuningIndex.push_back(t);
workspace_size = std::max(workspace_size, tmpWorkspaceSize);
foundAlgo = true;
}
CHECK_RETURNED_WORKSPACE_SIZE(workspace_size, max_workspace_size);
if(foundAlgo)
{
break;
}
}
};
auto collectAllSupportedAlgosViaCAPI
= [&](std::vector<hipblasLtMatmulHeuristicResult_t>& candidates,
bool& foundAlgo) {
foundAlgo = false;
for(int j = 0; j < returnedAlgoCount; j++)
{
size_t tmpWorkspaceSize = 0;
if(hipblaslt_ext::matmulIsAlgoSupported(handle,
matmul[0][0],
alpha_in[0],
matA[0],
matB[0],
&h_beta[0],
matC[0],
matD[0],
candidates[j].algo,
tmpWorkspaceSize)
== HIPBLAS_STATUS_SUCCESS
&& tmpWorkspaceSize <= max_workspace_size)
{
heuristicResult.push_back(candidates[j]);
heuristicTuningIndex.push_back(0);
workspace_size = std::max(workspace_size, tmpWorkspaceSize);
foundAlgo = true;
}
CHECK_RETURNED_WORKSPACE_SIZE(workspace_size, max_workspace_size);
}
};
auto trySelectFromBatch
= [&](std::vector<hipblasLtMatmulHeuristicResult_t>& candidates,
bool& foundAlgo) {
returnedAlgoCount = candidates.size();
foundAlgo = false;
if(do_grouped_gemm)
{
configureGroupedGemmForCurrentProblem();
collectTuningsForFirstViableAlgo(
candidates, groupedGemmVec[0], foundAlgo);
return;
}
if(arg.use_ext && batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
configureExtGemmForCurrentProblem();
collectTuningsForFirstViableAlgo(candidates, gemmVec[0], foundAlgo);
return;
}
collectAllSupportedAlgosViaCAPI(candidates, foundAlgo);
};
while(auto batch = nextBatchOfIndices())
{
std::vector<hipblasLtMatmulHeuristicResult_t> candidates;
fetchAlgosForBatch(*batch, candidates);
if(candidates.empty())
{
break;
}
selectionWasAttempted = true;
bool foundAlgo = false;
trySelectFromBatch(candidates, foundAlgo);
if(foundAlgo)
{
break;
}
}
};
auto verifyMixedValidityContract = [&]() {
auto verifyShape = [&](const char* shapeName,
const std::vector<int>& indices,
hipblasStatus_t expectedStatus,
size_t expectedValidCount) {
std::vector<hipblasLtMatmulHeuristicResult_t> mixedOut;
std::vector<int> indicesCopy = indices;
const auto status
= hipblaslt_ext::getAlgosFromIndex(handle, indicesCopy, mixedOut);
if(status != expectedStatus || mixedOut.size() != expectedValidCount)
{
hipblaslt_cerr
<< "verifyMixedValidityContract[" << shapeName
<< "]: status=" << hipblas_status_to_string(status) << " (expected "
<< hipblas_status_to_string(expectedStatus)
<< "), mixedOut.size()=" << mixedOut.size() << " (expected "
<< expectedValidCount << ")" << std::endl;
}
EXPECT_HIPBLAS_STATUS(status, expectedStatus);
#ifdef GOOGLE_TEST
EXPECT_EQ(mixedOut.size(), expectedValidCount) << "shape: " << shapeName;
#endif
};
const size_t numValid = validIndices.size();
{
std::vector<int> shape = validIndices;
shape.push_back(std::numeric_limits<int>::max());
verifyShape("trailing-invalid", shape, HIPBLAS_STATUS_INVALID_VALUE, numValid);
}
{
std::vector<int> shape = validIndices;
shape.insert(shape.begin() + (numValid / 2),
std::numeric_limits<int>::max());
verifyShape("middle-invalid", shape, HIPBLAS_STATUS_INVALID_VALUE, numValid);
}
{
std::vector<int> shape = validIndices;
std::reverse(shape.begin(), shape.end());
verifyShape("reversed", shape, HIPBLAS_STATUS_SUCCESS, numValid);
}
{
std::vector<int> shape;
shape.reserve(2 * numValid);
for(int idx : validIndices)
{
shape.push_back(idx);
shape.push_back(idx);
}
verifyShape("duplicated", shape, HIPBLAS_STATUS_SUCCESS, 2 * numValid);
}
{
verifyShape("empty", {}, HIPBLAS_STATUS_SUCCESS, 0);
}
{
// INT_MIN is avoided here because under HIPBLASLT_USE_ROCROLLER negative
// indices route to a different (rocroller) code path; two large positive
// out-of-range values pin the all-miss return in both build configs.
const std::vector<int> shape{std::numeric_limits<int>::max(),
std::numeric_limits<int>::max() - 1};
verifyShape("all-invalid", shape, HIPBLAS_STATUS_INVALID_VALUE, 0);
}
};
searchForSupportedAlgoViaIndexAPI();
if(!indicesAreDiscovered)
{
if(!selectionWasAttempted)
{
hipblaslt_cerr
<< "MatmulAlgoIndex: explicit solution_index=" << arg.solution_index
<< " returned no candidates from getAlgosFromIndex() (M=" << M[0]
<< " N=" << N[0] << " K=" << K[0] << " batch=" << num_batches[0]
<< " transA=" << arg.transA << " transB=" << arg.transB
<< " a=" << hip_datatype_to_string(arg.a_type)
<< " b=" << hip_datatype_to_string(arg.b_type)
<< " c=" << hip_datatype_to_string(arg.c_type)
<< " d=" << hip_datatype_to_string(arg.d_type)
<< " compute=" << hipblas_computetype_to_string(arg.compute_type) << ")"
<< std::endl;
CHECK_SOLUTION_FOUND(0);
}
else
{
CHECK_SOLUTION_FOUND(heuristicResult.size());
}
}
else if(!validIndices.empty())
{
if(heuristicResult.empty())
{
hipblaslt_cerr
<< "MatmulAlgoIndex: " << validIndices.size()
<< " algo indices discovered via getAllAlgos() but none produced a "
"viable algo+tuning under the workspace budget (M="
<< M[0] << " N=" << N[0] << " K=" << K[0]
<< " batch=" << num_batches[0] << " transA=" << arg.transA
<< " transB=" << arg.transB
<< " a=" << hip_datatype_to_string(arg.a_type)
<< " b=" << hip_datatype_to_string(arg.b_type)
<< " c=" << hip_datatype_to_string(arg.c_type)
<< " d=" << hip_datatype_to_string(arg.d_type)
<< " compute=" << hipblas_computetype_to_string(arg.compute_type) << ")"
<< std::endl;
CHECK_SOLUTION_FOUND(0);
}
verifyMixedValidityContract();
}
}
else if(arg.algo_method == 1)
{
std::vector<hipblasLtMatmulHeuristicResult_t> tmpAlgo;
EXPECT_HIPBLAS_STATUS(hipblaslt_ext::getAllAlgos(handle,
gemmType,
transA,
transB,
arg.a_type,
arg.b_type,
arg.c_type,
arg.d_type,
arg.compute_type,
tmpAlgo),
HIPBLAS_STATUS_SUCCESS);
returnedAlgoCount = tmpAlgo.size();
heuristicResult.clear();
heuristicTuningIndex.clear();
int requestCount = 0;
if(!do_grouped_gemm)
{
if(arg.use_ext && batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
if(arg.use_ext_setproblem)
{
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(gemmVec[b].setProblem(M[0],
N[0],
K[0],
num_batches[0],
lda[0],
ldb[0],
ldc[0],
ldd[0],
stride_da[0],
stride_db[0],
stride_c[0],
stride_d[0],
extepilogue[0],
extinputs[b][0],
extproblemtype));
}
else
{
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(gemmVec[b].setProblem(
matmul[b][0],
alpha_in[0],
(dA[0].as<char>()) + b * size_dA[0] * realDataTypeSize(TiA),
matA[0],
(dB[0].as<char>()) + b * size_dB[0] * realDataTypeSize(TiB),
matB[0],
&h_beta[0],
(dC[0].as<char>()) + b * size_C[0] * realDataTypeSize(To),
matC[0],
((*dDp)[0].as<char>()) + b * size_D[0] * realDataTypeSize(To),
matD[0]));
}
for(int j = 0; j < returnedAlgoCount; j++)
{
int addRequest = 0;
for(size_t t = 0; t < tuningVec.size(); t++)
{
size_t tmpWorkspaceSize = 0;
if(gemmVec[0].isAlgoSupported(
tmpAlgo[j].algo, tuningVec[t], tmpWorkspaceSize)
== HIPBLAS_STATUS_SUCCESS)
{
if(tmpWorkspaceSize <= max_workspace_size)
{
addRequest = 1;
heuristicResult.push_back(tmpAlgo[j]);
heuristicTuningIndex.push_back(t);
workspace_size = std::max(workspace_size, tmpWorkspaceSize);
}
}
}
CHECK_RETURNED_WORKSPACE_SIZE(workspace_size, max_workspace_size);
requestCount += addRequest;
if(requestCount >= requestAlgoCount)
{
break;
}
}
}
else
{
for(int j = 0; j < returnedAlgoCount; j++)
{
int addRequest = 0;
for(size_t t = 0; t < 1; t++) // C API not supported yet
{
size_t tmpWorkspaceSize = 0;
tmpAlgo[j].algo.max_workspace_bytes = max_workspace_size;
if(hipblaslt_ext::matmulIsAlgoSupported(handle,
matmul[0][0],
alpha_in[0],
matA[0],
matB[0],
&h_beta[0],
matC[0],
matD[0],
tmpAlgo[j].algo,
tmpWorkspaceSize)
== HIPBLAS_STATUS_SUCCESS)
{
if(tmpWorkspaceSize <= max_workspace_size)
{
addRequest = 1;
heuristicResult.push_back(tmpAlgo[j]);
heuristicTuningIndex.push_back(t);
workspace_size = std::max(workspace_size, tmpWorkspaceSize);
}
}
}
CHECK_RETURNED_WORKSPACE_SIZE(workspace_size, max_workspace_size);
requestCount += addRequest;
if(requestCount >= requestAlgoCount)
{
break;
}
}
}
}
else
{
if(arg.use_ext_setproblem)
{
auto num_batches_64 = std::vector<int64_t>{num_batches.begin(), num_batches.end()};
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].setProblem(M,
N,
K,
num_batches_64,
lda,
ldb,
ldc,
ldd,
stride_da,
stride_db,
stride_c,
stride_d,
extepilogue,
extinputs[b],
extproblemtype));
}
else
{
std::vector<void*> h_alpha_void, h_beta_void;
for(size_t i = 0; i < h_alpha.size(); i++)
{
h_alpha_void.push_back(&h_alpha[i]);
h_beta_void.push_back(&h_beta[i]);
}
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].setProblem(matmul[b],
h_alpha_void,
da[b],
matA,
db[b],
matB,
h_beta_void,
dc[b],
matC,
dd[b],
matD));
}
for(int j = 0; j < returnedAlgoCount; j++)
{
int addRequest = 0;
size_t tmpWorkspaceSize = 0;
for(size_t t = 0; t < tuningVec.size(); t++)
{
if(groupedGemmVec[0].isAlgoSupported(
tmpAlgo[j].algo, tuningVec[t], tmpWorkspaceSize)
== HIPBLAS_STATUS_SUCCESS)
{
if(tmpWorkspaceSize <= max_workspace_size)
{
addRequest = 1;
heuristicResult.push_back(tmpAlgo[j]);
heuristicTuningIndex.push_back(t);
workspace_size = std::max(workspace_size, tmpWorkspaceSize);
}
}
}
CHECK_RETURNED_WORKSPACE_SIZE(workspace_size, max_workspace_size);
requestCount += addRequest;
if(requestCount >= requestAlgoCount)
{
break;
}
}
}
}
else
{
std::vector<hipblasLtMatmulHeuristicResult_t> tmpAlgo;
if(!do_grouped_gemm)
{
if(arg.use_ext && batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
if(arg.use_ext_setproblem)
{
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(gemmVec[b].setProblem(M[0],
N[0],
K[0],
num_batches[0],
lda[0],
ldb[0],
ldc[0],
ldd[0],
stride_da[0],
stride_db[0],
stride_c[0],
stride_d[0],
extepilogue[0],
extinputs[b][0],
extproblemtype));
}
else
{
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(gemmVec[b].setProblem(
matmul[b][0],
alpha_in[0],
(dA[0].as<char>()) + b * size_dA[0] * realDataTypeSize(TiA),
matA[0],
(dB[0].as<char>()) + b * size_dB[0] * realDataTypeSize(TiB),
matB[0],
&h_beta[0],
(dC[0].as<char>()) + b * size_C[0] * realDataTypeSize(To),
matC[0],
((*dDp)[0].as<char>()) + b * size_D[0] * realDataTypeSize(To),
matD[0]));
}
CHECK_HIPBLASLT_ERROR(
gemmVec[0].algoGetHeuristic(requestAlgoCount, gemmPref, tmpAlgo));
heuristicResult.clear();
heuristicTuningIndex.clear();
for(int j = 0; j < tmpAlgo.size(); j++)
{
for(size_t t = 0; t < tuningVec.size(); t++)
{
size_t tmpWorkspaceSize = 0;
if(gemmVec[0].isAlgoSupported(
tmpAlgo[j].algo, tuningVec[t], tmpWorkspaceSize)
== HIPBLAS_STATUS_SUCCESS)
{
if(tmpWorkspaceSize <= max_workspace_size)
{
heuristicResult.push_back(tmpAlgo[j]);
heuristicTuningIndex.push_back(t);
workspace_size = std::max(workspace_size, tmpWorkspaceSize);
}
}
}
CHECK_RETURNED_WORKSPACE_SIZE(workspace_size, max_workspace_size);
}
returnedAlgoCount = heuristicResult.size();
}
else
{
std::vector<hipblasLtMatmulHeuristicResult_t> tmpAlgo(requestAlgoCount);
EXPECT_HIPBLAS_STATUS((hipblasLtMatmulAlgoGetHeuristic(handle,
matmul[0][0],
matA[0],
matB[0],
matC[0],
matD[0],
pref,
requestAlgoCount,
tmpAlgo.data(),
&returnedAlgoCount)),
HIPBLAS_STATUS_SUCCESS);
heuristicResult.clear();
for(int32_t i = 0; i < returnedAlgoCount; i++)
{
heuristicResult.push_back(tmpAlgo[i]);
}
heuristicTuningIndex.resize(heuristicResult.size(), 0); // C API not supported yet
}
for(int i = 0; i < returnedAlgoCount; i++)
workspace_size = std::max(workspace_size, heuristicResult[i].workspaceSize);
CHECK_RETURNED_WORKSPACE_SIZE(workspace_size, max_workspace_size);
}
else
{
if(arg.use_ext_setproblem)
{
auto num_batches_64 = std::vector<int64_t>{num_batches.begin(), num_batches.end()};
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].setProblem(M,
N,
K,
num_batches_64,
lda,
ldb,
ldc,
ldd,
stride_da,
stride_db,
stride_c,
stride_d,
extepilogue,
extinputs[b],
extproblemtype));
}
else
{
std::vector<void*> h_alpha_void, h_beta_void;
for(size_t i = 0; i < h_alpha.size(); i++)
{
h_alpha_void.push_back(&h_alpha[i]);
h_beta_void.push_back(&h_beta[i]);
}
for(int32_t b = 0; b < block_count; b++)
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].setProblem(matmul[b],
h_alpha_void,
da[b],
matA,
db[b],
matB,
h_beta_void,
dc[b],
matC,
dd[b],
matD));
}
CHECK_HIPBLASLT_ERROR(
groupedGemmVec[0].algoGetHeuristic(requestAlgoCount, gemmPref, tmpAlgo));
heuristicResult.clear();
heuristicTuningIndex.clear();
for(int j = 0; j < tmpAlgo.size(); j++)
{
for(size_t t = 0; t < tuningVec.size(); t++)
{
size_t tmpWorkspaceSize = 0;
if(groupedGemmVec[0].isAlgoSupported(
tmpAlgo[j].algo, tuningVec[t], tmpWorkspaceSize)
== HIPBLAS_STATUS_SUCCESS)
{
heuristicResult.push_back(tmpAlgo[j]);
heuristicTuningIndex.push_back(t);
}
}
}
workspace_size = max_workspace_size;
}
}
returnedAlgoCount = heuristicResult.size();
CHECK_SOLUTION_FOUND(returnedAlgoCount);
dWorkspace = new device_vector<unsigned char>(workspace_size * block_count, 1, HMM);
CHECK_DEVICE_ALLOCATION(dWorkspace->memcheck());
if(arg.use_user_args)
{
CHECK_HIP_ERROR(
hipHostMalloc(&userArgs, gemm_count * sizeof(hipblaslt_ext::UserArguments)));
CHECK_HIP_ERROR(hipMalloc(&d_userArgs,
block_count * gemm_count * sizeof(hipblaslt_ext::UserArguments)));
}
auto ptrs = benchmark_allocation();
if(arg.adaptive)
{
hipblaslt_bench::TimingConfig cfg;
cfg.warmup_time = arg.warmup_time;
cfg.sample_time = arg.sample_time;
cfg.measure_time = arg.measure_time;
cfg.max_measure_time = arg.max_measure_time;
cfg.min_iters = arg.min_iters;
cfg.max_iters = arg.max_iters;
cfg.noise_threshold = arg.noise_threshold;
cfg.stability_threshold = arg.stability_threshold;
cfg.stability_window = arg.stability_window;
cfg.stability_interval = arg.stability_interval;
hipblaslt_cout << hipblaslt_bench::format_adaptive_timing_summary(cfg) << std::endl;
}
if(arg.print_solution_found)
hipblaslt_cout << "Is supported " << heuristicResult.size()
<< " / Total solutions: " << returnedAlgoCount * tuningVec.size()
<< std::endl;
if(heuristicResult.size() != heuristicTuningIndex.size())
{
hipblaslt_cerr << "Internal error, heuristicResult.size() != heuristicTuningIndex.size() "
<< heuristicResult.size() << " != " << heuristicTuningIndex.size()
<< std::endl;
exit(EXIT_FAILURE);
}
// get CPU result
if(arg.unit_check || arg.norm_check || arg.allclose_check)
{
if(arg.timing)
{
cpu_time_used = get_time_us_no_sync();
}
#define epilogue_param \
M[gemmIdx], N[gemmIdx], ldd[gemmIdx], \
(hD_gold_epl[gemmIdx].as<char>() + pos * realDataTypeSize(Talpha)), \
(hD_gold[gemmIdx].as<char>() + pos * realDataTypeSize(To)), \
(hBias_gold_epl[gemmIdx].as<char>() + pos * realDataTypeSize(Talpha)), \
arg.amaxD ? hAmaxD_gold[gemmIdx].as<char>() + 0 : nullptr, ePos, Taux, scaleDValue, \
scaleEValue, applyBias
gemm_count = std::max(1, arg.grouped_gemm); //Resetting the gemm_count for GroupedGemm
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
auto alpha = h_alpha[gemmIdx];
auto betaTemp = h_beta[gemmIdx];
computeTypeInterface tempSC{};
if(arg.scaleC)
{
// betaTemp *= hScaleC[gemmIdx][0];
set_computeInterface(tempSC, hScaleC[gemmIdx].buf(), Tc, TiA);
mul_computeInterface(betaTemp, tempSC, Tc, TiA);
}
computeTypeInterface scale{};
set_computeInterface(scale, 1, Talpha, TiA);
void* scaleAVec = (arg.scaleA == hipblaslt_scaling_format::Scalar
|| arg.scaleA == hipblaslt_scaling_format::Vector)
? hScaleA[gemmIdx].buf()
: (void*)(&scale);
void* scaleBVec = (arg.scaleB == hipblaslt_scaling_format::Scalar
|| arg.scaleB == hipblaslt_scaling_format::Vector)
? hScaleB[gemmIdx].buf()
: (void*)(&scale);
void* scaleDValue = arg.scaleD ? hScaleD[gemmIdx].buf() : (void*)(&scale);
void* scaleEValue = arg.scaleE ? hScaleE[gemmIdx].buf() : (void*)(&scale);
bool const isScaleAMXFormat = isBlockScaling(arg.scaleA);
bool const isScaleBMXFormat = isBlockScaling(arg.scaleB);
for(int batchIdx = 0; batchIdx < num_batches[gemmIdx]; batchIdx++)
{
if(epilogue_on[gemmIdx])
{
// Note: for MX types, pass the reference float instead so there is
// no need to convert them to float in cblas_gemm
cblas_gemm(
transA,
transB,
M[gemmIdx],
N[gemmIdx],
K[gemmIdx],
alpha,
isScaleAMXFormat
? reinterpret_cast<char*>(refA[gemmIdx].data())
+ stride_a[gemmIdx] * batchIdx * realDataTypeSize(HIP_R_32F)
: hA[gemmIdx].as<char>()
+ stride_a[gemmIdx] * batchIdx * realDataTypeSize(TiA),
lda[gemmIdx],
isScaleBMXFormat
? reinterpret_cast<char*>(refB[gemmIdx].data())
+ stride_b[gemmIdx] * batchIdx * realDataTypeSize(HIP_R_32F)
: hB[gemmIdx].as<char>()
+ stride_b[gemmIdx] * batchIdx * realDataTypeSize(TiB),
ldb[gemmIdx],
betaTemp,
hD_gold_epl[gemmIdx].as<char>()
+ stride_d[gemmIdx] * batchIdx * realDataTypeSize(Talpha),
ldd[gemmIdx],
arg.scaleAlpha_vector ? hScaleAlphaVec[gemmIdx].as<char>() + 0 : nullptr,
scaleAVec,
scaleBVec,
(void*)(&scale),
(arg.scaleA == hipblaslt_scaling_format::Vector),
(arg.scaleB == hipblaslt_scaling_format::Vector),
isScaleAMXFormat ? HIP_R_32F : TiA,
isScaleBMXFormat ? HIP_R_32F : TiB,
Tc,
Tc,
isScaleAMXFormat ? HIP_R_32F : TciA,
isScaleBMXFormat ? HIP_R_32F : TciB,
false,
isBlockScaling(arg.scaleA),
isBlockScaling(arg.scaleB));
auto pos = stride_d[gemmIdx] * batchIdx;
std::vector<HipHostBuffer>* hEInst = arg.gradient ? &hE : &hE_gold;
void* ePos
= ((*hEInst).size() <= gemmIdx)
? nullptr
: ((*hEInst)[gemmIdx].as<char>() + pos * realDataTypeSize(Taux));
auto applyBias = arg.gradient ? false : arg.bias_vector;
void* hBias_buf = ((hBias).size() <= gemmIdx) ? nullptr : hBias[gemmIdx].buf();
if(applyBias && arg.bias_stride > 0)
{
hBias_buf = ((char*)hBias_buf) + (arg.bias_stride * batchIdx * realDataTypeSize(Tbias));
}
switch(arg.activation_type)
{
case hipblaslt_activation_type::gelu:
if(arg.gradient)
epilogue_func(epilogue_param,
hBias_buf,
Tbias,
arg.activation_arg1,
arg.activation_arg2,
::_dgelu,
true,
To,
Talpha);
else
{
epilogue_func(epilogue_param,
hBias_buf,
Tbias,
arg.activation_arg1,
arg.activation_arg2,
::_gelu,
false,
To,
Talpha);
}
break;
case hipblaslt_activation_type::relu:
if(arg.gradient)
{
epilogue_func(epilogue_param,
hBias_buf,
Tbias,
arg.activation_arg1,
arg.activation_arg2,
::_drelu,
true,
To,
Talpha);
}
else
{
epilogue_func(epilogue_param,
hBias_buf,
Tbias,
arg.activation_arg1,
arg.activation_arg2,
::_relu,
false,
To,
Talpha);
}
break;
case hipblaslt_activation_type::swish:
epilogue_func(epilogue_param,
hBias_buf,
Tbias,
arg.activation_arg1,
arg.activation_arg2,
::_silu,
arg.gradient,
To,
Talpha);
break;
case hipblaslt_activation_type::clamp:
epilogue_func(epilogue_param,
hBias_buf,
Tbias,
arg.activation_arg1,
arg.activation_arg2,
::_clamp,
arg.gradient,
To,
Talpha);
break;
default:
epilogue_func(epilogue_param, hBias_buf, Tbias, false, To, Talpha);
break;
}
if(arg.gradient && arg.bias_vector && batchIdx == num_batches[gemmIdx] - 1)
{
auto *hBias_gold_buf = hBias_gold[gemmIdx].buf();
if(arg.bias_stride > 0 && hBias_gold_buf != nullptr)
{
hBias_gold_buf = (char*)hBias_gold_buf + arg.bias_stride * batchIdx * realDataTypeSize(Tbias);
}
if(arg.bias_source == hipblaslt_bias_source::d)
{
reduction_func<false, float>(hBias_gold_epl[gemmIdx].as<char>()
+ pos * realDataTypeSize(Talpha),
Talpha,
hBias_gold_buf,
Tbias,
M[gemmIdx],
N[gemmIdx],
1,
ldd[gemmIdx],
stride_d[gemmIdx],
num_batches[gemmIdx]);
}
else
{
bool sumLd = false;
int s1 = 1, s2 = 1, s3 = 1;
auto reduc = [&sumLd,
&s1,
&s2,
&s3,
&hBias_gold_buf,
&Tbias,
&size_bias,
&K,
&num_batches,
&gemmIdx,
&arg](void* ptr, hipDataType Ti) {
if(sumLd)
{
reduction_func<true, float>(ptr,
Ti,
hBias_gold_buf,
Tbias,
size_bias[gemmIdx],
K[gemmIdx],
s1,
s2,
s3,
num_batches[gemmIdx]);
}
else
{
reduction_func<false, float>(ptr,
Ti,
hBias_gold_buf,
Tbias,
size_bias[gemmIdx],
K[gemmIdx],
s1,
s2,
s3,
num_batches[gemmIdx]);
}
};
if(arg.bias_source == hipblaslt_bias_source::a)
{
void* ptr = hA[gemmIdx].buf();
s2 = lda[gemmIdx];
s3 = stride_a[gemmIdx];
sumLd = transA == HIPBLAS_OP_N ? false : true;
reduc(ptr, TiA);
}
else if(arg.bias_source == hipblaslt_bias_source::b)
{
void* ptr = hB[gemmIdx].buf();
s2 = ldb[gemmIdx];
s3 = stride_b[gemmIdx];
sumLd = transB == HIPBLAS_OP_N ? true : false;
reduc(ptr, TiB);
}
}
}
}
else if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) //For General Batch GEMM
{
// Note: for MX types, pass the reference float instead so there is
// no need to convert them to float in cblas_gemm
// Added this logic to mimic the rocblas test quick_gemm_batched_bad_arg_f32_r_bad_arg_F
// This rocblas test passes alpha, A and B as 0 but beta as non-zero with valid C and D
// To mimic this behavior if --sizek is passed as 0 in hipblaslt-bench for --batch_mode 1, size_dA and size_dB
// will be set to 0 since A is MxK and B is KxN. In this case, we pass the pointer array A and B for
// General batched GEMM as nullptr and introduced an explicit check for AddressA and AddressB != 0
// in KernelWriterAssembly.py since the dereference of AddressA and AddressB for
// General Batched GEMM happens before the alphaNonZero check.
void *ptrA = (size_dA[0]) ? hA[batchIdx].as<char>() : nullptr;
void *ptrB = (size_dB[0]) ? hB[batchIdx].as<char>() : nullptr;
cblas_gemm(transA,
transB,
M[gemmIdx],
N[gemmIdx],
K[gemmIdx],
alpha,
ptrA,
lda[gemmIdx],
ptrB,
ldb[gemmIdx],
betaTemp,
hD_gold[batchIdx].as<char>(),
ldd[gemmIdx],
nullptr,
scaleAVec,
scaleBVec,
scaleDValue,
(arg.scaleA == hipblaslt_scaling_format::Vector),
(arg.scaleB == hipblaslt_scaling_format::Vector),
isScaleAMXFormat ? HIP_R_32F : TiA,
isScaleBMXFormat ? HIP_R_32F : TiB,
To,
Tc,
isScaleAMXFormat ? HIP_R_32F : TciA,
isScaleBMXFormat ? HIP_R_32F : TciB,
false,
isBlockScaling(arg.scaleA),
isBlockScaling(arg.scaleB));
}
else
{
// Note: for MX types, pass the reference float instead so there is
// no need to convert them to float in cblas_gemm
cblas_gemm(
transA,
transB,
M[gemmIdx],
N[gemmIdx],
K[gemmIdx],
alpha,
isScaleAMXFormat
? reinterpret_cast<char*>(refA[gemmIdx].data())
+ stride_a[gemmIdx] * batchIdx * realDataTypeSize(HIP_R_32F)
: hA[gemmIdx].as<char>()
+ stride_a[gemmIdx] * batchIdx * realDataTypeSize(TiA),
lda[gemmIdx],
isScaleBMXFormat
? reinterpret_cast<char*>(refB[gemmIdx].data())
+ stride_b[gemmIdx] * batchIdx * realDataTypeSize(HIP_R_32F)
: hB[gemmIdx].as<char>()
+ stride_b[gemmIdx] * batchIdx * realDataTypeSize(TiB),
ldb[gemmIdx],
betaTemp,
hD_gold[gemmIdx].as<char>()
+ stride_d[gemmIdx] * batchIdx * realDataTypeSize(To),
ldd[gemmIdx],
nullptr,
scaleAVec,
scaleBVec,
scaleDValue,
(arg.scaleA == hipblaslt_scaling_format::Vector),
(arg.scaleB == hipblaslt_scaling_format::Vector),
isScaleAMXFormat ? HIP_R_32F : TiA,
isScaleBMXFormat ? HIP_R_32F : TiB,
To,
Tc,
isScaleAMXFormat ? HIP_R_32F : TciA,
isScaleBMXFormat ? HIP_R_32F : TciB,
false,
isBlockScaling(arg.scaleA),
isBlockScaling(arg.scaleB));
}
}
}
if(arg.timing)
{
cpu_time_used = get_time_us_no_sync() - cpu_time_used;
}
}
void* alpha_ptr = nullptr;
void* beta_ptr = nullptr;
if(gemm_count > 0)
{
if(TiA == HIP_C_32F || TiA == HIP_C_64F)
{
if(TiA == HIP_C_32F)
{
alpha_ptr = arg.scaleAlpha_vector ? (void*)dScaleAlphaVec[0].buf()
: (void*)&(h_alpha[0].cf);
beta_ptr = (void*)&(h_beta[0].cf);
}
else if(TiA == HIP_C_64F)
{
alpha_ptr = arg.scaleAlpha_vector ? (void*)dScaleAlphaVec[0].buf()
: (void*)&(h_alpha[0].cd);
beta_ptr = (void*)&(h_beta[0].cd);
}
}
else
{
switch(Tc)
{
case HIP_R_32F:
alpha_ptr = arg.scaleAlpha_vector ? (void*)dScaleAlphaVec[0].buf()
: (void*)&(h_alpha[0].f32);
beta_ptr = (void*)&(h_beta[0].f32);
break;
case HIP_R_64F:
alpha_ptr = arg.scaleAlpha_vector ? (void*)dScaleAlphaVec[0].buf()
: (void*)&(h_alpha[0].f64);
beta_ptr = (void*)&(h_beta[0].f64);
break;
case HIP_R_16F:
alpha_ptr = arg.scaleAlpha_vector ? (void*)dScaleAlphaVec[0].buf()
: (void*)&(h_alpha[0].f16);
beta_ptr = (void*)&(h_beta[0].f16);
break;
case HIP_R_32I:
alpha_ptr = arg.scaleAlpha_vector ? (void*)dScaleAlphaVec[0].buf()
: (void*)&(h_alpha[0].i32);
beta_ptr = (void*)&(h_beta[0].i32);
break;
default:
hipblaslt_cerr << "FATAL: Unsupported type in pointer setup for hipblasLtMatmul"
<< std::endl;
alpha_ptr = nullptr;
beta_ptr = nullptr;
}
}
}
if(!arg.timing)
{
for(size_t sol = 0; sol < heuristicResult.size(); sol++)
{
if((arg.unit_check || arg.norm_check || arg.allclose_check) && arg.c_equal_d)
{
if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) // Iterate for batch_count for General Batched GEMM
{
for(int i = 0; i < arg.batch_count; i++)
{
CHECK_HIP_ERROR(synchronize(dC[i], hC[i], block_count));
}
}
else
{
for(int i = 0; i < gemm_count; i++)
{
CHECK_HIP_ERROR(synchronize(dC[i], hC[i], block_count));
}
}
}
if(!do_grouped_gemm)
{
if(arg.use_ext && batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
gemmVec[0].setMaxWorkspaceBytes(workspace_size);
CHECK_HIPBLASLT_ERROR(
gemmVec[0].initialize(heuristicResult[sol].algo,
tuningVec[heuristicTuningIndex[sol]],
*dWorkspace));
CHECK_HIPBLASLT_ERROR(gemmVec[0].run(stream));
}
else if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) //For General Batch GEMM
{
CHECK_HIP_ERROR(hipStreamSynchronize(stream));
// Added this logic to mimic the rocblas test quick_gemm_batched_bad_arg_f32_r_bad_arg_F
// This rocblas test passes alpha, A and B as 0 but beta as non-zero with valid C and D
// To mimic this behavior if --sizek is passed as 0 in hipblaslt-bench for --batch_mode 1, size_dA and size_dB
// will be set to 0 since A is MxK and B is KxN. In this case, we pass the pointer array A and B for
// General batched GEMM as nullptr and introduced an explicit check for AddressA and AddressB != 0
// in KernelWriterAssembly.py since the dereference of AddressA and AddressB for
// General Batched GEMM happens before the alphaNonZero check.
void *ptrA = (size_dA[0]) ? dda[0] : nullptr;
void *ptrB = (size_dB[0]) ? ddb[0] : nullptr;
EXPECT_HIPBLAS_STATUS(hipblasLtMatmul(handle,
matmul[0][0],
alpha_in[0],
ptrA,
matA[0],
ptrB,
matB[0],
&(h_beta[0]),
ddc[0],
matC[0],
ddd[0],
matD[0],
&heuristicResult[sol].algo,
*dWorkspace,
workspace_size,
stream),
HIPBLAS_STATUS_SUCCESS);
}
else
{
CHECK_HIP_ERROR(hipStreamSynchronize(stream));
EXPECT_HIPBLAS_STATUS(hipblasLtMatmul(handle,
matmul[0][0],
alpha_ptr,
dA[0].buf(),
matA[0],
dB[0].buf(),
matB[0],
beta_ptr,
dC[0].buf(),
matC[0],
(*dDp)[0].buf(),
matD[0],
&heuristicResult[sol].algo,
*dWorkspace,
workspace_size,
stream),
HIPBLAS_STATUS_SUCCESS);
}
}
else
{
//grouped gemm
if(arg.use_user_args)
{
groupedGemmVec[0].setMaxWorkspaceBytes(workspace_size);
CHECK_HIPBLASLT_ERROR(
groupedGemmVec[0].initialize(heuristicResult[sol].algo,
tuningVec[heuristicTuningIndex[0]],
*dWorkspace));
groupedGemmVec[0].getDefaultValueForDeviceUserArguments(userArgs);
// Copy them to device memory
CHECK_HIP_ERROR(hipMemcpy(d_userArgs,
userArgs,
gemm_count * sizeof(hipblaslt_ext::UserArguments),
hipMemcpyHostToDevice));
CHECK_HIPBLASLT_ERROR(groupedGemmVec[0].run(d_userArgs, stream));
}
else
{
groupedGemmVec[0].setMaxWorkspaceBytes(workspace_size);
CHECK_HIPBLASLT_ERROR(
groupedGemmVec[0].initialize(heuristicResult[sol].algo,
tuningVec[heuristicTuningIndex[0]],
*dWorkspace,
false,
stream));
CHECK_HIPBLASLT_ERROR(groupedGemmVec[0].run(stream));
}
}
double hipblaslt_error = 0.0;
double hipblaslt_atol = 1;
double hipblaslt_rtol = 1;
double hipblaslt_max_ulp = 0.0;
double hipblaslt_avg_ulp = 0.0;
std::vector<double> tol(gemm_count);
if(arg.unit_check && (hipblaslt_get_arch_major() == 11) && realDataTypeSize(TiA) == 2
&& realDataTypeSize(TiB) == 2)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
tol[gemmIdx] = K[gemmIdx] * sum_error_tolerance_for_gfx11_type(Tc, TiA, To);
}
}
if(arg.initialization == hipblaslt_initialization::integer_exact)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
tol[gemmIdx] = 0;
}
else if(arg.initialization == hipblaslt_initialization::fp16_accumulator_probe)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
tol[gemmIdx] = 1e-2;
}
else if(arg.initialization == hipblaslt_initialization::norm_dist_one_special)
{
// Gaussian-filled inputs + batched GEMM: use near_check like fp16_accumulator_probe
// (CPU ref vs GPU are not always bit-identical for f32/f16 accumulations).
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
tol[gemmIdx] = 1e-2;
}
if(arg.unit_check || arg.norm_check || arg.allclose_check)
{
if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) //For General Batch GEMM
{
copy_gemm_to_host(stream, arg.batch_count, hD_1, (*dDp));
}
else
{
copy_gemm_to_host(stream, gemm_count, hD_1, (*dDp));
}
check(stream,
arg,
gemm_count,
M,
N,
ldd,
lde,
stride_d,
stride_e,
num_batches,
size_bias,
hD_gold,
hD_1,
(*dDp),
hAmaxD_gold,
hAmaxD,
dAmaxD,
hE_gold,
hE,
dE,
hBias_gold,
hBias,
dBias,
tol,
hipblaslt_error,
hipblaslt_atol,
hipblaslt_rtol,
hipblaslt_max_ulp,
hipblaslt_avg_ulp,
To,
Tbias,
Taux,
Talpha,
batchMode);
}
}
}
else
{
// Get device information
hipDeviceProp_t deviceProps;
CHECK_HIP_ERROR(hipGetDeviceProperties(&deviceProps, 0));
int32_t gpu_block3 = deviceProps.multiProcessorCount * 60;
size_t best_sol = -1;
double best_flops = 0.0;
double best_gpu_time = std::numeric_limits<double>::max();
double best_warm_time = std::numeric_limits<double>::max();
std::string best_s_name = "";
std::string best_k_name = "";
double best_norm = 0.0;
double best_atol = 0.0;
double best_rtol = 0.0;
double best_max_ulp = 0.0;
double best_avg_ulp = 0.0;
int number_cold_calls
= ((arg.unit_check || arg.norm_check || arg.allclose_check) && arg.cold_iters == 0)
? 1
: arg.cold_iters;
// Adaptive timing ignores --cold_iters/--iters: warmup and batch are sized
// inside run_measurement. Keep one cold call only when a result copy or the
// skip-slow screen needs it.
if(arg.adaptive)
number_cold_calls = (arg.unit_check || arg.norm_check || arg.allclose_check
|| arg.skip_slow_solution_ratio != 0.0f)
? 1
: 0;
int number_hot_calls = arg.iters;
// Adaptive timing configuration shared by every solution measured below.
// Adaptive timing is gated on --adaptive; the per-knob settings apply only then.
hipblaslt_bench::TimingConfig timingCfg;
timingCfg.iters = number_hot_calls;
timingCfg.use_gpu_timer = arg.use_gpu_timer;
timingCfg.adaptive = arg.adaptive;
if(arg.adaptive)
{
timingCfg.warmup_time = arg.warmup_time;
timingCfg.sample_time = arg.sample_time;
timingCfg.measure_time = arg.measure_time;
timingCfg.max_measure_time = arg.max_measure_time;
timingCfg.min_iters = arg.min_iters;
timingCfg.max_iters = arg.max_iters;
timingCfg.noise_threshold = arg.noise_threshold;
timingCfg.stability_threshold = arg.stability_threshold;
timingCfg.stability_window = arg.stability_window;
timingCfg.stability_interval = arg.stability_interval;
}
if(arg.adaptive)
{
if(const auto err = hipblaslt_bench::validate_adaptive_config(timingCfg); !err.empty())
{
hipblaslt_cerr << "error: invalid adaptive timing config: " << err << std::endl;
return;
}
}
hipblaslt_bench::TimingResult timing;
// Stop the sample loop if a launch hits a gtest fatal failure.
auto timingAbort = []() -> bool {
#ifdef GOOGLE_TEST
return ::testing::Test::HasFatalFailure();
#else
return false;
#endif
};
hipblaslt_bench::TimingResult best_timing;
int flush_iter = 100000;
double flush_time_used = 0;
if(arg.flush)
{
static std::unordered_map<std::string, double> flush_times_cache;
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
std::string device_uuid(deviceProps.uuid.bytes);
if(!flush_times_cache.count(device_uuid))
{
for(int i = 0; i < flush_iter; i++)
hipLaunchKernelGGL(flush_icache, dim3(gpu_block3), dim3(64), 0, stream);
pre_gpu_time(arg.use_gpu_timer, event_gpu_time_start, flush_time_used, stream);
for(int i = 0; i < flush_iter; i++)
hipLaunchKernelGGL(flush_icache, dim3(gpu_block3), dim3(64), 0, stream);
post_gpu_time(arg.use_gpu_timer,
event_gpu_time_start,
event_gpu_time_end,
flush_time_used,
stream);
flush_time_used /= flush_iter;
flush_times_cache[device_uuid] = flush_time_used;
}
else
{
flush_time_used = flush_times_cache[device_uuid];
}
}
timingCfg.flush_us = flush_time_used;
for(size_t sol = 0; sol < heuristicResult.size(); sol++)
{
// Reset per-solution so an aborted/empty measurement can't report the prior
// solution's stats (run_measurement leaves `out` untouched on early return).
timing = {};
if((arg.unit_check || arg.norm_check || arg.allclose_check) && arg.c_equal_d)
{
if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) //For General Batch GEMM
{
for(int i = 0; i < arg.batch_count; i++)
{
CHECK_HIP_ERROR(synchronize(dC[i], hC[i], block_count));
}
}
else
{
for(int i = 0; i < gemm_count; i++)
{
CHECK_HIP_ERROR(synchronize(dC[i], hC[i], block_count));
}
}
}
if(!do_grouped_gemm)
{
auto perf_monitor = EfficiencyMonitor::create();
if(arg.use_ext && batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
for(int32_t b = 0; b < block_count; b++)
{
gemmVec[b].setMaxWorkspaceBytes(workspace_size);
CHECK_HIPBLASLT_ERROR(
gemmVec[b].initialize(heuristicResult[sol].algo,
tuningVec[heuristicTuningIndex[sol]],
*dWorkspace));
}
if(arg.skip_slow_solution_ratio)
pre_gpu_time(
arg.use_gpu_timer, event_gpu_time_start, gpu_time_used, stream);
for(int i = 0; i < number_cold_calls; i++)
{
CHECK_HIPBLASLT_ERROR(gemmVec[i % block_count].run(stream));
if(i == 0 && (arg.unit_check || arg.norm_check || arg.allclose_check))
copy_gemm_to_host(stream, gemm_count, hD_1, (*dDp));
}
if(arg.skip_slow_solution_ratio)
{
post_gpu_time(arg.use_gpu_timer,
event_gpu_time_start,
event_gpu_time_end,
gpu_time_used,
stream);
best_warm_time
= best_warm_time < gpu_time_used ? best_warm_time : gpu_time_used;
if((gpu_time_used * arg.skip_slow_solution_ratio) > best_warm_time)
{
hipblaslt_cout
<< std::setprecision(2) << "Skip solution: " << sol
<< " (best warm-up = " << best_warm_time / number_cold_calls
<< " us , warm-up = " << gpu_time_used / number_cold_calls
<< " us, skip ratio = " << arg.skip_slow_solution_ratio << ")"
<< std::endl;
continue;
}
}
perf_monitor->start();
hipblaslt_bench::run_measurement(
[&](int64_t i) {
int b = static_cast<int>(i % block_count);
CHECK_HIPBLASLT_ERROR(gemmVec[b].run(stream));
if(arg.flush)
hipLaunchKernelGGL(
flush_icache, dim3(gpu_block3), dim3(64), 0, stream);
},
timingCfg,
event_gpu_time_start,
event_gpu_time_end,
stream,
timing,
timingAbort);
}
else if(batchMode == HIPBLASLT_BATCH_MODE_POINTER_ARRAY) //For General Batch GEMM
{
if(arg.skip_slow_solution_ratio)
pre_gpu_time(
arg.use_gpu_timer, event_gpu_time_start, gpu_time_used, stream);
for(int i = 0; i < number_cold_calls; i++)
{
auto ptr_matmul = matmul[i % block_count][0];
auto ptr_alpha = arg.scaleAlpha_vector
? (dScaleAlphaVec[0].as<char>())
+ (i % block_count) * size_scaleAlphaVec[0]
: alpha_in[0];
// Added this logic to mimic the rocblas test quick_gemm_batched_bad_arg_f32_r_bad_arg_F
// This rocblas test passes alpha, A and B as 0 but beta as non-zero with valid C and D
// To mimic this behavior if --sizek is passed as 0 in hipblaslt-bench for --batch_mode 1, size_dA and size_dB
// will be set to 0 since A is MxK and B is KxN. In this case, we pass the pointer array A and B for
// General batched GEMM as nullptr and introduced an explicit check for AddressA and AddressB != 0
// in KernelWriterAssembly.py since the dereference of AddressA and AddressB for
// General Batched GEMM happens before the alphaNonZero check.
void *ptrA = (size_dA[0]) ? dda[i % block_count] : nullptr;
void *ptrB = (size_dB[0]) ? ddb[i % block_count] : nullptr;
EXPECT_HIPBLAS_STATUS(hipblasLtMatmul(handle,
ptr_matmul,
ptr_alpha,
ptrA,
matA[0],
ptrB,
matB[0],
&(h_beta[0]),
ddc[i % block_count],
matC[0],
ddd[i % block_count],
matD[0],
&heuristicResult[sol].algo,
*dWorkspace,
workspace_size,
stream),
HIPBLAS_STATUS_SUCCESS);
if(i == 0 && (arg.unit_check || arg.norm_check || arg.allclose_check))
copy_gemm_to_host(stream, arg.batch_count, hD_1, (*dDp));
}
if(arg.skip_slow_solution_ratio)
{
post_gpu_time(arg.use_gpu_timer,
event_gpu_time_start,
event_gpu_time_end,
gpu_time_used,
stream);
best_warm_time
= best_warm_time < gpu_time_used ? best_warm_time : gpu_time_used;
if((gpu_time_used * arg.skip_slow_solution_ratio) > best_warm_time)
{
hipblaslt_cout
<< std::setprecision(2) << "Skip solution: " << sol
<< " (best warm-up = " << best_warm_time / number_cold_calls
<< " us , warm-up = " << gpu_time_used / number_cold_calls
<< " us, skip ratio = " << arg.skip_slow_solution_ratio << ")"
<< std::endl;
continue;
}
}
perf_monitor->start();
hipblaslt_bench::run_measurement(
[&](int64_t i) {
int b = static_cast<int>(i % block_count);
auto ptr_matmul = matmul[b][0];
auto ptr_alpha = arg.scaleAlpha_vector
? (dScaleAlphaVec[0].as<char>())
+ b * size_scaleAlphaVec[0]
: alpha_in[0];
void* ptrA = (size_dA[0]) ? dda[b] : nullptr;
void* ptrB = (size_dB[0]) ? ddb[b] : nullptr;
EXPECT_HIPBLAS_STATUS(hipblasLtMatmul(handle,
ptr_matmul,
ptr_alpha,
ptrA,
matA[0],
ptrB,
matB[0],
&(h_beta[0]),
ddc[b],
matC[0],
ddd[b],
matD[0],
&heuristicResult[sol].algo,
*dWorkspace,
workspace_size,
stream),
HIPBLAS_STATUS_SUCCESS);
if(arg.flush)
hipLaunchKernelGGL(
flush_icache, dim3(gpu_block3), dim3(64), 0, stream);
},
timingCfg,
event_gpu_time_start,
event_gpu_time_end,
stream,
timing,
timingAbort);
}
else
{
if(arg.skip_slow_solution_ratio)
pre_gpu_time(
arg.use_gpu_timer, event_gpu_time_start, gpu_time_used, stream);
for(int i = 0; i < number_cold_calls; i++)
{
auto ptr_matmul = matmul[i % block_count][0];
auto ptr_alpha = arg.scaleAlpha_vector
? (dScaleAlphaVec[0].as<char>())
+ (i % block_count) * size_scaleAlphaVec[0]
: alpha_in[0];
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmul(
handle,
ptr_matmul,
alpha_ptr,
dA[0].as<char>()
+ (i % block_count) * size_dA[0] * realDataTypeSize(TiA),
matA[0],
dB[0].as<char>()
+ (i % block_count) * size_dB[0] * realDataTypeSize(TiB),
matB[0],
beta_ptr,
dC[0].as<char>()
+ (i % block_count) * size_C[0] * realDataTypeSize(To),
matC[0],
(*dDp)[0].as<char>()
+ (i % block_count) * size_D[0] * realDataTypeSize(To),
matD[0],
&heuristicResult[sol].algo,
*dWorkspace,
workspace_size,
stream),
HIPBLAS_STATUS_SUCCESS);
if(i == 0 && (arg.unit_check || arg.norm_check || arg.allclose_check))
copy_gemm_to_host(stream, gemm_count, hD_1, (*dDp));
}
if(arg.skip_slow_solution_ratio)
{
post_gpu_time(arg.use_gpu_timer,
event_gpu_time_start,
event_gpu_time_end,
gpu_time_used,
stream);
best_warm_time
= best_warm_time < gpu_time_used ? best_warm_time : gpu_time_used;
if((gpu_time_used * arg.skip_slow_solution_ratio) > best_warm_time)
{
hipblaslt_cout
<< std::setprecision(2) << "Skip solution: " << sol
<< " (best warm-up = " << best_warm_time / number_cold_calls
<< " us , warm-up = " << gpu_time_used / number_cold_calls
<< " us, skip ratio = " << arg.skip_slow_solution_ratio << ")"
<< std::endl;
continue;
}
}
perf_monitor->start();
hipblaslt_bench::run_measurement(
[&](int64_t i) {
int b = static_cast<int>(i % block_count);
auto ptr_matmul = matmul[b][0];
auto ptr_alpha = arg.scaleAlpha_vector
? (dScaleAlphaVec[0].as<char>())
+ b * size_scaleAlphaVec[0]
: alpha_in[0];
EXPECT_HIPBLAS_STATUS(
hipblasLtMatmul(
handle,
ptr_matmul,
alpha_ptr,
dA[0].as<char>() + b * size_dA[0] * realDataTypeSize(TiA),
matA[0],
dB[0].as<char>() + b * size_dB[0] * realDataTypeSize(TiB),
matB[0],
beta_ptr,
dC[0].as<char>() + b * size_C[0] * realDataTypeSize(To),
matC[0],
(*dDp)[0].as<char>() + b * size_D[0] * realDataTypeSize(To),
matD[0],
&heuristicResult[sol].algo,
*dWorkspace,
workspace_size,
stream),
HIPBLAS_STATUS_SUCCESS);
if(arg.flush)
hipLaunchKernelGGL(
flush_icache, dim3(gpu_block3), dim3(64), 0, stream);
},
timingCfg,
event_gpu_time_start,
event_gpu_time_end,
stream,
timing,
timingAbort);
}
gpu_time_used = timing.median_us;
perf_monitor->stop();
}
else
{
auto perf_monitor = EfficiencyMonitor::create();
if(arg.use_user_args)
{
std::vector<unsigned char*> d_userArgsVec(block_count);
//grouped gemm
for(int32_t b = 0; b < block_count; b++)
{
groupedGemmVec[b].setMaxWorkspaceBytes(workspace_size);
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].initialize(
heuristicResult[sol].algo,
tuningVec[heuristicTuningIndex[sol]],
((unsigned char*)(*dWorkspace) + b * workspace_size)));
groupedGemmVec[b].getDefaultValueForDeviceUserArguments(userArgs);
d_userArgsVec[b] = (unsigned char*)d_userArgs
+ b * gemm_count * sizeof(hipblaslt_ext::UserArguments);
// Copy them to device memory
CHECK_HIP_ERROR(hipMemcpy(d_userArgsVec[b],
userArgs,
gemm_count * sizeof(hipblaslt_ext::UserArguments),
hipMemcpyHostToDevice));
}
if(arg.skip_slow_solution_ratio)
pre_gpu_time(
arg.use_gpu_timer, event_gpu_time_start, gpu_time_used, stream);
for(int i = 0; i < number_cold_calls; i++)
{
CHECK_HIPBLASLT_ERROR(groupedGemmVec[i % block_count].run(
d_userArgsVec[i % block_count], stream));
if(i == 0 && (arg.unit_check || arg.norm_check || arg.allclose_check))
copy_gemm_to_host(stream, gemm_count, hD_1, (*dDp));
}
if(arg.skip_slow_solution_ratio)
{
post_gpu_time(arg.use_gpu_timer,
event_gpu_time_start,
event_gpu_time_end,
gpu_time_used,
stream);
best_warm_time
= best_warm_time < gpu_time_used ? best_warm_time : gpu_time_used;
if((gpu_time_used * arg.skip_slow_solution_ratio) > best_warm_time)
{
hipblaslt_cout
<< std::setprecision(2) << "Skip solution: " << sol
<< " (best warm-up = " << best_warm_time / number_cold_calls
<< " us , warm-up = " << gpu_time_used / number_cold_calls
<< " us, skip ratio = " << arg.skip_slow_solution_ratio << ")"
<< std::endl;
continue;
}
}
perf_monitor->start();
hipblaslt_bench::run_measurement(
[&](int64_t i) {
int b = static_cast<int>(i % block_count);
CHECK_HIPBLASLT_ERROR(
groupedGemmVec[b].run(d_userArgsVec[b], stream));
},
timingCfg,
event_gpu_time_start,
event_gpu_time_end,
stream,
timing,
timingAbort);
gpu_time_used = timing.median_us;
perf_monitor->stop();
}
else
{
//grouped gemm
for(int32_t b = 0; b < block_count; b++)
{
groupedGemmVec[b].setMaxWorkspaceBytes(workspace_size);
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].initialize(
heuristicResult[sol].algo,
tuningVec[heuristicTuningIndex[sol]],
((unsigned char*)(*dWorkspace) + b * workspace_size),
false,
stream));
}
if(arg.skip_slow_solution_ratio)
pre_gpu_time(
arg.use_gpu_timer, event_gpu_time_start, gpu_time_used, stream);
for(int i = 0; i < number_cold_calls; i++)
{
CHECK_HIPBLASLT_ERROR(groupedGemmVec[i % block_count].run(stream));
if(i == 0 && (arg.unit_check || arg.norm_check || arg.allclose_check))
copy_gemm_to_host(stream, gemm_count, hD_1, (*dDp));
}
if(arg.skip_slow_solution_ratio)
{
post_gpu_time(arg.use_gpu_timer,
event_gpu_time_start,
event_gpu_time_end,
gpu_time_used,
stream);
best_warm_time
= best_warm_time < gpu_time_used ? best_warm_time : gpu_time_used;
if((gpu_time_used * arg.skip_slow_solution_ratio) > best_warm_time)
{
hipblaslt_cout
<< std::setprecision(2) << "Skip solution: " << sol
<< " (best warm-up = " << best_warm_time / number_cold_calls
<< " us , warm-up = " << gpu_time_used / number_cold_calls
<< " us, skip ratio = " << arg.skip_slow_solution_ratio << ")"
<< std::endl;
continue;
}
}
perf_monitor->start();
hipblaslt_bench::run_measurement(
[&](int64_t i) {
int b = static_cast<int>(i % block_count);
CHECK_HIPBLASLT_ERROR(groupedGemmVec[b].run(stream));
},
timingCfg,
event_gpu_time_start,
event_gpu_time_end,
stream,
timing,
timingAbort);
gpu_time_used = timing.median_us;
perf_monitor->stop();
}
}
double flops = 0;
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
flops += gemm_gflop_count(M[gemmIdx], N[gemmIdx], K[gemmIdx], Talpha);
switch(arg.activation_type)
{
case hipblaslt_activation_type::relu:
flops += relu_gflop_count(M[gemmIdx], N[gemmIdx], Talpha);
break;
case hipblaslt_activation_type::gelu:
flops += gelu_gflop_count(M[gemmIdx], N[gemmIdx], Talpha);
break;
case hipblaslt_activation_type::swish:
flops += silu_gflop_count(M[gemmIdx], N[gemmIdx], Talpha);
break;
case hipblaslt_activation_type::clamp:
flops += clamp_gflop_count(M[gemmIdx], N[gemmIdx], Talpha);
break;
case hipblaslt_activation_type::sigmoid:
flops += sigmoid_gflop_count(M[gemmIdx], N[gemmIdx], Talpha);
break;
default:
break;
}
}
double hipblaslt_error = 0.0;
double hipblaslt_atol = 1;
double hipblaslt_rtol = 1;
double hipblaslt_max_ulp = 0.0;
double hipblaslt_avg_ulp = 0.0;
std::vector<double> tol(gemm_count);
if(arg.unit_check && (hipblaslt_get_arch_major() == 11) && realDataTypeSize(TiA) == 2
&& realDataTypeSize(TiB) == 2)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
{
hipblaslt_cout << "k = " << K[gemmIdx] << "\n";
tol[gemmIdx] = K[gemmIdx] * sum_error_tolerance_for_gfx11_type(Tc, TiA, To);
}
}
if(arg.initialization == hipblaslt_initialization::integer_exact)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
tol[gemmIdx] = 0;
}
else if(arg.initialization == hipblaslt_initialization::fp16_accumulator_probe)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
tol[gemmIdx] = 1e-2;
}
else if(arg.initialization == hipblaslt_initialization::norm_dist_one_special)
{
for(int gemmIdx = 0; gemmIdx < gemm_count; gemmIdx++)
tol[gemmIdx] = 1e-2;
}
if(arg.unit_check || arg.norm_check || arg.allclose_check)
{
if(arg.dump_matrix)
{
for(int batchId = 0; batchId < num_batches[0]; batchId++)
{
hipblasltDispatchValuesToFile(HIPBLAS_OP_N,
To,
M[0],
N[0],
ldd[0],
hD_1[0].as<char>() + batchId * stride_d[0] * realDataTypeSize(To),
"batch_" + std::to_string(batchId) + "_D_output.txt");
hipblasltDispatchValuesToFile(HIPBLAS_OP_N,
To,
M[0],
N[0],
ldd[0],
hD_gold[0].as<char>() + batchId * stride_d[0] * realDataTypeSize(To),
"batch_" + std::to_string(batchId) + "_D_Gold_output.txt");
}
}
check(stream,
arg,
gemm_count,
M,
N,
ldd,
lde,
stride_d,
stride_e,
num_batches,
size_bias,
hD_gold,
hD_1,
(*dDp),
hAmaxD_gold,
hAmaxD,
dAmaxD,
hE_gold,
hE,
dE,
hBias_gold,
hBias,
dBias,
tol,
hipblaslt_error,
hipblaslt_atol,
hipblaslt_rtol,
hipblaslt_max_ulp,
hipblaslt_avg_ulp,
To,
Tbias,
Taux,
Talpha,
batchMode);
}
#define argument_param \
e_transA, e_transB, e_grouped_gemm, e_batch_count, e_M, e_N, e_K, e_alpha, e_lda, e_stride_a, \
e_beta, e_ldb, e_stride_b, e_ldc, e_stride_c, e_ldd, e_stride_d, e_a_type, e_b_type, \
e_c_type, e_d_type, e_compute_type, e_scaleA, e_scaleB, e_scaleC, e_scaleD, e_amaxD, \
e_swizzle_a, e_swizzle_b, e_activation_type, e_bias_vector, e_bias_type, e_aux_type
const char* tuningEnv = getenv("HIPBLASLT_TUNING_FILE");
int32_t solutionIndex = ((tuningEnv && heuristicResult.size() == 1)
|| (arg.print_solution_found && arg.print_kernel_info))
? hipblaslt_ext::getIndexFromAlgo(heuristicResult[sol].algo)
: -1;
std::string solutionName = "";
std::string kernelName = "";
std::string archName = "";
std::string cuNum = "";
if(tuningEnv && heuristicResult.size() == 1)
{
archName = deviceProps.gcnArchName;
cuNum = std::to_string(deviceProps.multiProcessorCount);
}
if(arg.print_solution_found)
{
if(arg.print_kernel_info)
{
if(arg.use_ext && batchMode != HIPBLASLT_BATCH_MODE_POINTER_ARRAY)
{
if(!do_grouped_gemm)
{
solutionName = gemmVec[0].getSolutionName();
kernelName = gemmVec[0].getKernelName();
}
else
{
solutionName = groupedGemmVec[0].getSolutionName();
kernelName = groupedGemmVec[0].getKernelName();
}
}
else
{
solutionName = hipblaslt_ext::getSolutionNameFromAlgo(
handle, heuristicResult[sol].algo);
kernelName = hipblaslt_ext::getKernelNameFromAlgo(
handle, heuristicResult[sol].algo);
}
}
ArgumentModel<argument_param>{}.log_args(
Talpha,
hipblaslt_cout,
sol,
solutionIndex,
solutionName,
kernelName,
archName,
cuNum,
arg,
(uint32_t)tuningVec[heuristicTuningIndex[sol]].getSplitK(),
(uint32_t)tuningVec[heuristicTuningIndex[sol]].getWgm(),
gpu_time_used,
flops,
gpu_mem_gbytes,
cpu_time_used,
hipblaslt_error,
hipblaslt_atol,
hipblaslt_rtol,
hipblaslt_max_ulp,
hipblaslt_avg_ulp,
timing);
}
if(best_gpu_time > gpu_time_used)
{
best_sol = sol;
best_flops = flops;
best_gpu_time = gpu_time_used;
best_s_name = solutionName;
best_k_name = kernelName;
best_norm = hipblaslt_error;
best_atol = hipblaslt_atol;
best_rtol = hipblaslt_rtol;
best_max_ulp = hipblaslt_max_ulp;
best_avg_ulp = hipblaslt_avg_ulp;
best_timing = timing;
}
}
if(heuristicResult.size() > 1)
{
const char* tuningEnv = getenv("HIPBLASLT_TUNING_FILE");
int32_t solutionIndex
= (tuningEnv || arg.print_kernel_info)
? hipblaslt_ext::getIndexFromAlgo(heuristicResult[best_sol].algo)
: -1;
std::string solutionName = "";
std::string kernelName = "";
std::string archName = "";
std::string cuNum = "";
if(tuningEnv)
{
archName = deviceProps.gcnArchName;
cuNum = std::to_string(deviceProps.multiProcessorCount);
}
if(arg.print_kernel_info)
{
solutionName = best_s_name;
kernelName = best_k_name;
}
hipblaslt_cout << "Winner: " << std::endl;
ArgumentModel<argument_param>{}.log_args(
Talpha,
hipblaslt_cout,
best_sol,
solutionIndex,
solutionName,
kernelName,
archName,
cuNum,
arg,
(uint32_t)tuningVec[heuristicTuningIndex[best_sol]].getSplitK(),
(uint32_t)tuningVec[heuristicTuningIndex[best_sol]].getWgm(),
best_gpu_time,
best_flops,
gpu_mem_gbytes,
cpu_time_used,
best_norm,
best_atol,
best_rtol,
best_max_ulp,
best_avg_ulp,
best_timing);
}
}
for(auto it : ptrs)
{
CHECK_HIP_ERROR(hipFree(it));
}
//Freeing the device memory allocated for the General Batched GEMM Pointer Arrays
for(int i = 0; i < block_count; i++)
{
CHECK_HIP_ERROR(hipFree(dda[i]));
CHECK_HIP_ERROR(hipFree(ddb[i]));
CHECK_HIP_ERROR(hipFree(ddc[i]));
CHECK_HIP_ERROR(hipFree(ddd[i]));
CHECK_HIP_ERROR(hipFreeHost(hha[i]));
CHECK_HIP_ERROR(hipFreeHost(hhb[i]));
CHECK_HIP_ERROR(hipFreeHost(hhc[i]));
CHECK_HIP_ERROR(hipFreeHost(hhd[i]));
}
if(dWorkspace != nullptr)
delete dWorkspace;
if(userArgs != nullptr)
CHECK_HIP_ERROR(hipFree(userArgs));
if(d_userArgs != nullptr)
CHECK_HIP_ERROR(hipFree(d_userArgs));
// Explicitly destroy opaque handles to avoid leaks.
for(auto& h : matA)
if(h)
(void)hipblasLtMatrixLayoutDestroy(h);
for(auto& h : matB)
if(h)
(void)hipblasLtMatrixLayoutDestroy(h);
for(auto& h : matC)
if(h)
(void)hipblasLtMatrixLayoutDestroy(h);
for(auto& h : matD)
if(h)
(void)hipblasLtMatrixLayoutDestroy(h);
for(auto& block : matmul)
for(auto& h : block)
if(h)
(void)hipblasLtMatmulDescDestroy(h);
CHECK_HIP_ERROR(hipStreamDestroy(stream));
CHECK_HIP_ERROR(hipEventDestroy(event_gpu_time_start));
CHECK_HIP_ERROR(hipEventDestroy(event_gpu_time_end));
}