@@ -0,0 +1,242 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2023 - 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \file
|
||||
\brief Transform Kernel Universal adapter
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// common
|
||||
#include "cutlass/cutlass.h"
|
||||
#include "cutlass/cluster_launch.hpp"
|
||||
#include "cutlass/device_kernel.h"
|
||||
#include "cutlass/cuda_host_adapter.hpp"
|
||||
|
||||
namespace cutlass::transform::device {
|
||||
|
||||
template <class TransformKernel_>
|
||||
class TransformUniversalAdapter
|
||||
{
|
||||
public:
|
||||
using TransformKernel = TransformKernel_;
|
||||
using Arguments = typename TransformKernel::Arguments;
|
||||
using Params = typename TransformKernel::Params;
|
||||
|
||||
private:
|
||||
Params params_;
|
||||
static constexpr bool const EnableCudaHostAdapter = CUTLASS_ENABLE_CUDA_HOST_ADAPTER;
|
||||
|
||||
public:
|
||||
Params const& params() const {
|
||||
return this->params_;
|
||||
}
|
||||
|
||||
static Status
|
||||
can_implement(Arguments const& args) {
|
||||
return TransformKernel::can_implement(args);
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_workspace_size(Arguments const& args) {
|
||||
return TransformKernel::get_workspace_size(args);
|
||||
}
|
||||
|
||||
static dim3
|
||||
get_grid_shape(Arguments const& args, void* workspace = nullptr) {
|
||||
auto tmp_params = TransformKernel::to_underlying_arguments(args, workspace);
|
||||
return TransformKernel::get_grid_shape(tmp_params);
|
||||
}
|
||||
|
||||
static dim3
|
||||
get_grid_shape(Params const& params) {
|
||||
return TransformKernel::get_grid_shape(params);
|
||||
}
|
||||
|
||||
Status
|
||||
initialize(
|
||||
Arguments & args,
|
||||
void* workspace = nullptr,
|
||||
cudaStream_t stream = nullptr,
|
||||
CudaHostAdapter *cuda_adapter = nullptr) {
|
||||
|
||||
CUTLASS_TRACE_HOST("TransformUniversalAdapter::initialize() - workspace "
|
||||
<< workspace << ", stream: " << (stream ? "non-null" : "null"));
|
||||
|
||||
// Initialize the workspace
|
||||
Status status = TransformKernel::initialize_workspace(args, workspace, stream, cuda_adapter);
|
||||
if (status != Status::kSuccess) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Initialize the Params structure
|
||||
this->params_ = TransformKernel::to_underlying_arguments(args, workspace);
|
||||
|
||||
// Don't set the function attributes - require the CudaHostAdapter to set it.
|
||||
if constexpr (EnableCudaHostAdapter) {
|
||||
CUTLASS_ASSERT(cuda_adapter);
|
||||
return Status::kSuccess;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Account for dynamic smem capacity if needed
|
||||
//
|
||||
int smem_size = TransformKernel::SharedStorageSize;
|
||||
|
||||
CUTLASS_ASSERT(cuda_adapter == nullptr);
|
||||
|
||||
if (smem_size >= (48 << 10)) {
|
||||
CUTLASS_TRACE_HOST(" Setting smem size to " << smem_size);
|
||||
cudaError_t result = cudaFuncSetAttribute(
|
||||
device_kernel<TransformKernel>,
|
||||
cudaFuncAttributeMaxDynamicSharedMemorySize,
|
||||
smem_size);
|
||||
if (cudaSuccess != result) {
|
||||
result = cudaGetLastError();
|
||||
CUTLASS_TRACE_HOST(" cudaFuncSetAttribute() returned error: " << cudaGetErrorString(result));
|
||||
return Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
static Status
|
||||
run(
|
||||
Params& params,
|
||||
cudaStream_t stream = nullptr,
|
||||
CudaHostAdapter *cuda_adapter = nullptr,
|
||||
int32_t kernel_index = 0) {
|
||||
|
||||
CUTLASS_TRACE_HOST("TransformUniversalAdapter::run()");
|
||||
dim3 const block = TransformKernel::get_block_shape();
|
||||
dim3 const grid = get_grid_shape(params);
|
||||
// Currently only support 1x1x1 for transform kernel.
|
||||
dim3 const cluster = {1,1,1};
|
||||
|
||||
// configure smem size and carveout
|
||||
int smem_size = TransformKernel::SharedStorageSize;
|
||||
|
||||
Status launch_result;
|
||||
|
||||
// Use extended launch API only for mainloops that use it
|
||||
if constexpr(TransformKernel::ArchTag::kMinComputeCapability >= 90) {
|
||||
void* kernel_params[] = {¶ms};
|
||||
|
||||
if constexpr (EnableCudaHostAdapter) {
|
||||
//
|
||||
// Use the cuda host adapter
|
||||
//
|
||||
CUTLASS_ASSERT(cuda_adapter);
|
||||
if (cuda_adapter) {
|
||||
launch_result = cuda_adapter->launch(
|
||||
grid, cluster, block, smem_size, stream, kernel_params, kernel_index);
|
||||
}
|
||||
else {
|
||||
return Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CUTLASS_ASSERT(cuda_adapter == nullptr);
|
||||
void const* kernel = (void const*) device_kernel<TransformKernel>;
|
||||
launch_result = ClusterLauncher::launch(
|
||||
grid, cluster, block, smem_size, stream, kernel, kernel_params);
|
||||
}
|
||||
}
|
||||
else {
|
||||
launch_result = Status::kSuccess;
|
||||
if constexpr (EnableCudaHostAdapter) {
|
||||
CUTLASS_ASSERT(cuda_adapter);
|
||||
if (cuda_adapter) {
|
||||
void* kernel_params[] = {¶ms};
|
||||
launch_result = cuda_adapter->launch(
|
||||
grid, block, smem_size, stream, kernel_params, 0);
|
||||
}
|
||||
else {
|
||||
return Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CUTLASS_ASSERT(cuda_adapter == nullptr);
|
||||
device_kernel<TransformKernel><<<grid, block, smem_size, stream>>>(params);
|
||||
}
|
||||
}
|
||||
|
||||
cudaError_t result = cudaGetLastError();
|
||||
if (cudaSuccess == result && Status::kSuccess == launch_result) {
|
||||
return Status::kSuccess;
|
||||
} else {
|
||||
CUTLASS_TRACE_HOST(" Kernel launch failed. Reason: " << result);
|
||||
return Status::kErrorInternal;
|
||||
}
|
||||
}
|
||||
|
||||
Status
|
||||
run(
|
||||
Arguments & args,
|
||||
void* workspace = nullptr,
|
||||
cudaStream_t stream = nullptr,
|
||||
CudaHostAdapter *cuda_adapter = nullptr,
|
||||
int32_t kernel_index = 0) {
|
||||
|
||||
Status status = initialize(args, workspace, stream, cuda_adapter);
|
||||
if (Status::kSuccess == status) {
|
||||
status = run(this->params_, stream, cuda_adapter, kernel_index);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status
|
||||
operator()(
|
||||
Arguments & args,
|
||||
void* workspace = nullptr,
|
||||
cudaStream_t stream = nullptr,
|
||||
CudaHostAdapter *cuda_adapter = nullptr) {
|
||||
return run(args, workspace, stream, cuda_adapter);
|
||||
}
|
||||
|
||||
Status
|
||||
run(
|
||||
cudaStream_t stream = nullptr,
|
||||
CudaHostAdapter *cuda_adapter = nullptr) {
|
||||
return run(this->params_, stream, cuda_adapter);
|
||||
}
|
||||
|
||||
Status
|
||||
operator()(
|
||||
cudaStream_t stream = nullptr,
|
||||
CudaHostAdapter *cuda_adapter = nullptr) {
|
||||
return run(this->params_, stream, cuda_adapter);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cutlass::transform::device
|
||||
@@ -0,0 +1,205 @@
|
||||
/***************************************************************************************************
|
||||
* Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**************************************************************************************************/
|
||||
|
||||
/* \file
|
||||
\brief Convolution filter format transformation kernel.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#include "cutlass/coord.h"
|
||||
#include "cutlass/arch/arch.h"
|
||||
#include "cutlass/layout/matrix.h"
|
||||
#include "cutlass/cuda_host_adapter.hpp"
|
||||
|
||||
#include "cute/int_tuple.hpp"
|
||||
#include "cute/tensor.hpp"
|
||||
#include "cute/config.hpp"
|
||||
|
||||
namespace cutlass::transform::kernel {
|
||||
|
||||
using namespace cute;
|
||||
|
||||
enum class FilterFormat {
|
||||
CKTRS,
|
||||
CTRSK,
|
||||
KTRSC
|
||||
};
|
||||
|
||||
template <
|
||||
FilterFormat SrcFormat,
|
||||
FilterFormat DstFormat,
|
||||
int NumDimensions,
|
||||
class Element,
|
||||
int AlignmentBytes = 16
|
||||
>
|
||||
struct ConvFilterFormatTransformer {
|
||||
static_assert(SrcFormat == FilterFormat::CKTRS, "Currently only source format of CKTRS is supported");
|
||||
static_assert(DstFormat == FilterFormat::CTRSK || DstFormat == FilterFormat::KTRSC, "Currently only destination format of CTRSK/KTRSC is supported");
|
||||
static_assert(AlignmentBytes % static_cast<int>(sizeof(Element)) == 0, "Invalid alignment setting");
|
||||
|
||||
// In ktrsc order.
|
||||
using FilterExtent = array<int, NumDimensions>;
|
||||
|
||||
// Default cta tile shape: 32x32
|
||||
static constexpr auto CTATileShape = make_shape(Int<4 * AlignmentBytes / static_cast<int>(sizeof(Element))>{}, Int<32>{});
|
||||
// Default thread layout: (4, 32)
|
||||
static constexpr auto ThreadLayout = make_layout(make_shape(Int<4>{}, Int<32>{}));
|
||||
|
||||
static constexpr uint32_t MaxThreadsPerBlock = 128;
|
||||
static constexpr uint32_t MinBlocksPerMultiprocessor = 1;
|
||||
|
||||
using ArchTag = arch::Sm90;
|
||||
|
||||
// Default ctor
|
||||
CUTLASS_HOST_DEVICE
|
||||
ConvFilterFormatTransformer() {}
|
||||
|
||||
struct Arguments {
|
||||
const void *src_ptr;
|
||||
void *dst_ptr;
|
||||
FilterExtent filter_extent;
|
||||
};
|
||||
|
||||
struct Params {
|
||||
using TensorSrc = decltype(make_tensor(make_gmem_ptr(recast_ptr<const Element>(nullptr)), make_layout(take<0,NumDimensions>(FilterExtent{}))));
|
||||
using TensorDst = decltype(make_tensor(make_gmem_ptr(recast_ptr<Element>(nullptr)), make_layout(make_shape(int32_t(0), int32_t(0)))));
|
||||
|
||||
TensorSrc src;
|
||||
TensorDst dst;
|
||||
};
|
||||
|
||||
struct SharedStorage {
|
||||
/* empty, no smem needed */
|
||||
};
|
||||
|
||||
static constexpr int SharedStorageSize = sizeof(SharedStorage);
|
||||
|
||||
static Status
|
||||
can_implement(Arguments const& args) {
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_workspace_size(Arguments const& args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static dim3
|
||||
get_block_shape() {
|
||||
return dim3(size(shape(ThreadLayout)), 1, 1);
|
||||
}
|
||||
|
||||
static dim3
|
||||
get_grid_shape(Params const& params) {
|
||||
auto dim_m = ceil_div(size<0>(shape(params.dst)), get<0>(CTATileShape));
|
||||
auto dim_n = ceil_div(size<1>(shape(params.dst)), get<1>(CTATileShape));
|
||||
|
||||
return dim3(dim_m, dim_n, 1);
|
||||
}
|
||||
|
||||
static cutlass::Status
|
||||
initialize_workspace(Arguments const& args, void* workspace = nullptr, cudaStream_t stream = nullptr,
|
||||
CudaHostAdapter *cuda_adapter = nullptr) {
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
static Params
|
||||
to_underlying_arguments(Arguments & args, void* workspace) {
|
||||
auto k = args.filter_extent[0];
|
||||
auto c = args.filter_extent[NumDimensions - 1];
|
||||
auto srt = reverse(take<1,NumDimensions - 1>(args.filter_extent));
|
||||
|
||||
// source shape (s,r,t,k,c)
|
||||
auto shape_src = flatten(make_shape(srt, k, c));
|
||||
auto shape_dst = DstFormat == FilterFormat::CTRSK ? make_shape(k, c * product(srt)) : make_shape(c, k * product(srt));
|
||||
|
||||
auto src = make_tensor(make_gmem_ptr(recast_ptr<const Element>(args.src_ptr)), make_layout(shape_src));
|
||||
auto dst = make_tensor(make_gmem_ptr(recast_ptr<Element>(args.dst_ptr)), make_layout(shape_dst));
|
||||
|
||||
return Params{src, dst};
|
||||
}
|
||||
|
||||
CUTLASS_DEVICE
|
||||
void operator()(Params const& params, char *smem_buf) {
|
||||
// Tile the input tensor into blocks
|
||||
auto block_coord = make_coord(blockIdx.x, blockIdx.y);
|
||||
auto block_shape = make_shape(Int<4 * AlignmentBytes / static_cast<int>(sizeof(Element))>{}, Int<32>{});
|
||||
// Default thread layout: (4, 32)
|
||||
auto thread_layout = make_layout(make_shape(Int<4>{}, Int<32>{}));
|
||||
auto vec_layout = make_layout(make_shape(Int<AlignmentBytes / static_cast<int>(sizeof(Element))>{}, Int<1>{}));
|
||||
|
||||
Tensor tile_D = local_tile(params.dst, block_shape, block_coord);
|
||||
|
||||
// Construct tiled copy
|
||||
using AccessType = cutlass::AlignedArray<Element, size(vec_layout)>;
|
||||
using Atom = Copy_Atom<UniversalCopy<AccessType>, Element>;
|
||||
|
||||
auto tiled_copy = make_tiled_copy(Atom{}, thread_layout, vec_layout);
|
||||
auto thr_copy = tiled_copy.get_thread_slice(threadIdx.x);
|
||||
Tensor thr_tile_D = thr_copy.partition_D(tile_D);
|
||||
|
||||
// shape (s, r, t)
|
||||
auto shape_trs = take<0, NumDimensions - 2>(shape(params.src));
|
||||
// strided_c = c for format CTRSK, strided_c = k for format KTRSC
|
||||
auto strided_c = DstFormat == FilterFormat::CTRSK ? get<NumDimensions - 1>(shape(params.src)) : get<NumDimensions - 2>(shape(params.src));
|
||||
// shape (s, r, t, c) for format CTRSK and shape (s, r, t, k) for format KTRSC
|
||||
auto shape_ctrs = append<NumDimensions - 1>(shape_trs, strided_c);
|
||||
auto srtc_coord = idx2crd(int(blockIdx.y * get<1>(block_shape) + threadIdx.x / size<0>(thread_layout)), shape_ctrs);
|
||||
// index of k for format CTRSK and index of c for format KTRSC
|
||||
auto n_layout = make_layout(make_shape(gridDim.x, size<0>(thread_layout)), make_stride(size<0>(block_shape), size<0>(vec_layout)));
|
||||
int n_idx = n_layout(make_coord(blockIdx.x, threadIdx.x % size<0>(thread_layout)));
|
||||
|
||||
// Fragment to load from S and store to D
|
||||
auto frag = make_fragment_like(thr_tile_D);
|
||||
// Predicate tensor.
|
||||
Tensor thr_tile_P = make_tensor<bool>(shape(thr_tile_D));
|
||||
|
||||
CUTLASS_PRAGMA_UNROLL
|
||||
for (int i = 0; i < size(frag); ++i) {
|
||||
auto srt_coord = take<0, NumDimensions - 2>(srtc_coord);
|
||||
auto kc_coord = DstFormat == FilterFormat::CTRSK ?
|
||||
make_coord(n_idx+i, get<NumDimensions - 2>(srtc_coord)) :
|
||||
make_coord(get<NumDimensions - 2>(srtc_coord), n_idx+i);
|
||||
auto coord = flatten(make_coord(srt_coord, kc_coord));
|
||||
frag(i) = params.src(coord);
|
||||
thr_tile_P(i) = elem_less(coord, shape(params.src));
|
||||
}
|
||||
|
||||
// Copy from RMEM to GMEM
|
||||
copy_if(tiled_copy, thr_tile_P, frag, thr_tile_D);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cutlass::transform::kernel
|
||||
Reference in New Issue
Block a user