Files
paddlepaddle--paddle/paddle/phi/kernels/gpu/strided_copy_kernel.cu
T
2026-07-13 12:40:42 +08:00

1051 lines
38 KiB
Plaintext

/* Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/phi/kernels/strided_copy_kernel.h"
#include <cstdint>
#include "paddle/common/enforce.h"
#include "paddle/phi/backends/gpu/gpu_launch_config.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/empty_kernel.h"
#include "paddle/phi/kernels/expand_kernel.h"
#include "paddle/phi/kernels/funcs/slice_utils.h"
#include "paddle/phi/kernels/funcs/strided_copy_kernel.cu.h"
namespace phi {
template <typename T, size_t RANK>
__global__ void StridedCopyCaseZeroFunc(
const T* input_data,
Array<int64_t, DDim::kMaxRank + 1> input_stride,
T* output_data,
Array<int64_t, DDim::kMaxRank + 1> output_stride) {
int64_t input_offset = 0;
int64_t output_offset = 0;
int64_t coordinate[6] = {threadIdx.x,
threadIdx.y,
threadIdx.z,
blockIdx.x,
blockIdx.y,
blockIdx.z};
#pragma unroll
for (int dim = RANK - 1; dim >= 0; --dim) {
input_offset += coordinate[RANK - 1 - dim] * input_stride[dim];
output_offset += coordinate[RANK - 1 - dim] * output_stride[dim];
}
output_data[output_offset] = input_data[input_offset];
}
template <typename T, typename Context>
bool LaunchStridedCopyCaseZeroKernel(
const Context& dev_ctx,
const T* input_data,
const Array<int64_t, DDim::kMaxRank + 1>& input_stride,
T* output_data,
const Array<int64_t, DDim::kMaxRank + 1>& output_stride,
const Array<int64_t, DDim::kMaxRank + 1>& dims,
int rank) {
if (rank > 6) {
return false;
}
dim3 grid(1, 1, 1), block(1, 1, 1);
if (rank >= 1) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 1], "strided_copy block.x");
block.x = static_cast<uint32_t>(dims[rank - 1]);
}
if (rank >= 2) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 2], "strided_copy block.y");
block.y = static_cast<uint32_t>(dims[rank - 2]);
}
if (rank >= 3) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 3], "strided_copy block.z");
block.z = static_cast<uint32_t>(dims[rank - 3]);
}
if (rank >= 4) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 4], "strided_copy grid.x");
grid.x = static_cast<uint32_t>(dims[rank - 4]);
}
if (rank >= 5) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 5], "strided_copy grid.y");
grid.y = static_cast<uint32_t>(dims[rank - 5]);
}
if (rank >= 6) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 6], "strided_copy grid.z");
grid.z = static_cast<uint32_t>(dims[rank - 6]);
}
if (!VerifyStridedCopyThreadConfigurationParameters(block, grid)) {
return false;
}
switch (rank) {
case 1:
StridedCopyCaseZeroFunc<T, 1><<<grid, block, 0, dev_ctx.stream()>>>(
input_data, input_stride, output_data, output_stride);
break;
case 2:
StridedCopyCaseZeroFunc<T, 2><<<grid, block, 0, dev_ctx.stream()>>>(
input_data, input_stride, output_data, output_stride);
break;
case 3:
StridedCopyCaseZeroFunc<T, 3><<<grid, block, 0, dev_ctx.stream()>>>(
input_data, input_stride, output_data, output_stride);
break;
case 4:
StridedCopyCaseZeroFunc<T, 4><<<grid, block, 0, dev_ctx.stream()>>>(
input_data, input_stride, output_data, output_stride);
break;
case 5:
StridedCopyCaseZeroFunc<T, 5><<<grid, block, 0, dev_ctx.stream()>>>(
input_data, input_stride, output_data, output_stride);
break;
case 6:
StridedCopyCaseZeroFunc<T, 6><<<grid, block, 0, dev_ctx.stream()>>>(
input_data, input_stride, output_data, output_stride);
break;
}
return true;
}
template <typename T, size_t N>
__global__ void StridedCopyCaseOneFunc(
const T* input_data,
Array<int64_t, DDim::kMaxRank + 1> input_stride,
T* out_data,
Array<int64_t, DDim::kMaxRank + 1> output_stride,
Array<int64_t, 6> dims,
const int64_t x_max) {
int64_t x = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
if (x < x_max) {
int64_t input_offset = 0;
int64_t output_offset = 0;
int64_t reg_dims[6] = {
dims[0], dims[1], dims[2], dims[3], dims[4], dims[5]};
int64_t coordinate[DDim::kMaxRank + 1];
switch (N) {
case 1:
coordinate[0] = x % reg_dims[0];
break;
case 2:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
break;
case 3:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
break;
case 4:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
break;
case 5:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
break;
case 6:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
break;
case 7:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
coordinate[6] = blockIdx.z % reg_dims[4];
break;
case 8:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
coordinate[6] = blockIdx.z % reg_dims[4];
coordinate[7] = blockIdx.z / reg_dims[4] % reg_dims[5];
break;
case 9:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
coordinate[6] = blockIdx.z % reg_dims[4];
coordinate[7] = blockIdx.z / reg_dims[4] % reg_dims[5];
coordinate[8] = blockIdx.z / (reg_dims[4] * reg_dims[5]);
break;
}
#pragma unroll
for (int dim = N - 1; dim >= 0; --dim) {
input_offset += coordinate[N - 1 - dim] * input_stride[dim];
output_offset += coordinate[N - 1 - dim] * output_stride[dim];
}
out_data[output_offset] = input_data[input_offset];
}
}
template <typename T, typename Context>
bool LaunchStridedCopyCaseOneKernel(
const Context& dev_ctx,
const T* input_data,
const Array<int64_t, DDim::kMaxRank + 1>& input_stride,
T* output_data,
const Array<int64_t, DDim::kMaxRank + 1>& output_stride,
const Array<int64_t, DDim::kMaxRank + 1>& dims,
int rank,
int64_t numel) {
dim3 grid(1, 1, 1), block(1, 1, 1);
Array<int64_t, 6> cur_dims;
block.x = 512;
if (rank >= 1) {
int64_t grid_x = (numel + static_cast<int64_t>(block.x) - 1) /
static_cast<int64_t>(block.x);
PADDLE_ENFORCE_LE_UINT32_MAX(grid_x, "strided_copy grid.x");
grid.x = static_cast<uint32_t>(grid_x);
cur_dims[0] = dims[rank - 1];
}
if (rank >= 2) {
cur_dims[1] = dims[rank - 2];
}
if (rank >= 4) {
int64_t grid_x = (dims[rank - 1] * dims[rank - 2] * dims[rank - 3] +
static_cast<int64_t>(block.x) - 1) /
static_cast<int64_t>(block.x);
PADDLE_ENFORCE_LE_UINT32_MAX(grid_x, "strided_copy grid.x");
grid.x = static_cast<uint32_t>(grid_x);
}
if (rank >= 6) {
int64_t grid_y = dims[rank - 4] * dims[rank - 5] * dims[rank - 6];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_y, "strided_copy grid.y");
grid.y = static_cast<uint32_t>(grid_y);
cur_dims[2] = dims[rank - 4];
cur_dims[3] = dims[rank - 5];
} else if (rank >= 5) {
int64_t grid_y = dims[rank - 4] * dims[rank - 5];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_y, "strided_copy grid.y");
grid.y = static_cast<uint32_t>(grid_y);
cur_dims[2] = dims[rank - 4];
cur_dims[3] = dims[rank - 5];
} else if (rank >= 4) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 4], "strided_copy grid.y");
grid.y = static_cast<uint32_t>(dims[rank - 4]);
cur_dims[2] = dims[rank - 4];
}
if (rank >= 9) {
int64_t grid_z = dims[rank - 7] * dims[rank - 8] * dims[rank - 9];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_z, "strided_copy grid.z");
grid.z = static_cast<uint32_t>(grid_z);
cur_dims[4] = dims[rank - 7];
cur_dims[5] = dims[rank - 8];
} else if (rank >= 8) {
int64_t grid_z = dims[rank - 7] * dims[rank - 8];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_z, "strided_copy grid.z");
grid.z = static_cast<uint32_t>(grid_z);
cur_dims[4] = dims[rank - 7];
cur_dims[5] = dims[rank - 8];
} else if (rank >= 7) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 7], "strided_copy grid.z");
grid.z = static_cast<uint32_t>(dims[rank - 7]);
cur_dims[4] = dims[rank - 7];
}
if (!VerifyStridedCopyThreadConfigurationParameters(block, grid)) {
return false;
}
switch (rank) {
case 1:
StridedCopyCaseOneFunc<T, 1>
<<<grid, block, 0, dev_ctx.stream()>>>(input_data,
input_stride,
output_data,
output_stride,
cur_dims,
dims[rank - 1]);
break;
case 2:
StridedCopyCaseOneFunc<T, 2><<<grid, block, 0, dev_ctx.stream()>>>(
input_data,
input_stride,
output_data,
output_stride,
cur_dims,
dims[rank - 1] * dims[rank - 2]);
break;
#define CASE_RANK(__Rk) \
case __Rk: \
StridedCopyCaseOneFunc<T, __Rk><<<grid, block, 0, dev_ctx.stream()>>>( \
input_data, \
input_stride, \
output_data, \
output_stride, \
cur_dims, \
dims[rank - 1] * dims[rank - 2] * dims[rank - 3]); \
break;
CASE_RANK(3);
CASE_RANK(4);
CASE_RANK(5);
CASE_RANK(6);
CASE_RANK(7);
CASE_RANK(8);
CASE_RANK(9);
#undef CASE_RANK
default:
PADDLE_THROW(common::errors::InvalidArgument(
"The rank of input should be less than 9, but received %d.", rank));
}
return true;
}
template <typename T, size_t RANK>
__global__ void StridedCopyDefaultFunc(
const T* input_data,
Array<int64_t, DDim::kMaxRank + 1> input_stride,
T* output_data,
Array<int64_t, DDim::kMaxRank + 1> output_stride,
Array<int64_t, DDim::kMaxRank + 1> dims,
const int64_t numel) {
int64_t gid =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
#pragma unroll
for (int64_t i = gid; i < numel; i += static_cast<int64_t>(blockDim.x) *
static_cast<int64_t>(gridDim.x)) {
int64_t input_offset = 0;
int64_t index_tmp = i;
#pragma unroll
for (int dim = RANK - 1; dim >= 0; --dim) {
input_offset += (index_tmp % dims[dim]) * input_stride[dim];
index_tmp = index_tmp / dims[dim];
}
int64_t output_offset = 0;
index_tmp = i;
#pragma unroll
for (int dim = RANK - 1; dim >= 0; --dim) {
output_offset += (index_tmp % dims[dim]) * output_stride[dim];
index_tmp = index_tmp / dims[dim];
}
output_data[output_offset] = input_data[input_offset];
}
}
template <typename T, typename Context>
void LaunchStridedCopyDefaultKernel(
const Context& dev_ctx,
const T* input_data,
const Array<int64_t, DDim::kMaxRank + 1>& input_stride,
T* output_data,
const Array<int64_t, DDim::kMaxRank + 1>& output_stride,
const Array<int64_t, DDim::kMaxRank + 1>& dims,
int rank,
int64_t numel) {
constexpr uint32_t block = 512;
const int64_t grid_x = (numel + block - 1) / block;
PADDLE_ENFORCE_LE_UINT32_MAX(grid_x, "strided_copy grid.x");
const dim3 grid(static_cast<uint32_t>(grid_x));
const dim3 block_dim(block);
switch (rank) {
#define CASE_RANK(__Rk) \
case __Rk: \
StridedCopyDefaultFunc<T, __Rk><<<grid, block_dim, 0, dev_ctx.stream()>>>( \
input_data, input_stride, output_data, output_stride, dims, numel); \
break;
CASE_RANK(1);
CASE_RANK(2);
CASE_RANK(3);
CASE_RANK(4);
CASE_RANK(5);
CASE_RANK(6);
CASE_RANK(7);
CASE_RANK(8);
CASE_RANK(9);
#undef CASE_RANK
default:
PADDLE_THROW(common::errors::InvalidArgument(
"The rank of input should be less than 9, but received %d.", rank));
}
}
template <typename T, size_t RANK>
__global__ void Strided2ContiguousCaseZeroFunc(
const T* input_data,
Array<int64_t, DDim::kMaxRank + 1> input_stride,
T* output_data) {
int64_t input_offset = 0;
int64_t output_offset =
(static_cast<int64_t>(blockIdx.z) * static_cast<int64_t>(gridDim.y) *
static_cast<int64_t>(gridDim.x) +
static_cast<int64_t>(blockIdx.y) * static_cast<int64_t>(gridDim.x) +
static_cast<int64_t>(blockIdx.x)) *
static_cast<int64_t>(blockDim.z) * static_cast<int64_t>(blockDim.y) *
static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.z) * static_cast<int64_t>(blockDim.y) *
static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.y) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t coordinate[6] = {threadIdx.x,
threadIdx.y,
threadIdx.z,
blockIdx.x,
blockIdx.y,
blockIdx.z};
#pragma unroll
for (int dim = RANK - 1; dim >= 0; --dim) {
input_offset += coordinate[RANK - 1 - dim] * input_stride[dim];
}
output_data[output_offset] = input_data[input_offset];
}
template <typename T, typename Context>
bool LaunchStrided2ContiguousCaseZeroKernel(
const Context& dev_ctx,
const T* input_data,
const Array<int64_t, DDim::kMaxRank + 1>& input_stride,
T* output_data,
const Array<int64_t, DDim::kMaxRank + 1>& dims,
int rank) {
if (rank > 6) {
return false;
}
dim3 grid(1, 1, 1), block(1, 1, 1);
if (rank >= 1) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 1], "strided_copy block.x");
block.x = static_cast<uint32_t>(dims[rank - 1]);
}
if (rank >= 2) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 2], "strided_copy block.y");
block.y = static_cast<uint32_t>(dims[rank - 2]);
}
if (rank >= 3) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 3], "strided_copy block.z");
block.z = static_cast<uint32_t>(dims[rank - 3]);
}
if (rank >= 4) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 4], "strided_copy grid.x");
grid.x = static_cast<uint32_t>(dims[rank - 4]);
}
if (rank >= 5) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 5], "strided_copy grid.y");
grid.y = static_cast<uint32_t>(dims[rank - 5]);
}
if (rank >= 6) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 6], "strided_copy grid.z");
grid.z = static_cast<uint32_t>(dims[rank - 6]);
}
if (!VerifyStridedCopyThreadConfigurationParameters(block, grid)) {
return false;
}
switch (rank) {
#define CASE_RANK(__Rk) \
case __Rk: \
Strided2ContiguousCaseZeroFunc<T, __Rk> \
<<<grid, block, 0, dev_ctx.stream()>>>( \
input_data, input_stride, output_data); \
break
CASE_RANK(1);
CASE_RANK(2);
CASE_RANK(3);
CASE_RANK(4);
CASE_RANK(5);
CASE_RANK(6);
#undef CASE_RANK
}
return true;
}
template <typename T, size_t N>
__global__ void Strided2ContiguousCaseOneFunc(
const T* input_data,
Array<int64_t, DDim::kMaxRank + 1> input_stride,
T* out_data,
Array<int64_t, 6> dims,
const int64_t x_max) {
int64_t x =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
if (x < x_max) {
int64_t input_offset = 0;
int64_t output_offset =
(static_cast<int64_t>(blockIdx.z) * static_cast<int64_t>(gridDim.y) +
static_cast<int64_t>(blockIdx.y)) *
x_max +
x;
int64_t reg_dims[6] = {
dims[0], dims[1], dims[2], dims[3], dims[4], dims[5]};
int64_t coordinate[DDim::kMaxRank + 1];
switch (N) {
case 1:
coordinate[0] = x % reg_dims[0];
break;
case 2:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
break;
case 3:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
break;
case 4:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
break;
case 5:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
break;
case 6:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
break;
case 7:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
coordinate[6] = blockIdx.z % reg_dims[4];
break;
case 8:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
coordinate[6] = blockIdx.z % reg_dims[4];
coordinate[7] = blockIdx.z / reg_dims[4] % reg_dims[5];
break;
case 9:
coordinate[0] = x % reg_dims[0];
coordinate[1] = x / reg_dims[0] % reg_dims[1];
coordinate[2] = x / (reg_dims[0] * reg_dims[1]);
coordinate[3] = blockIdx.y % reg_dims[2];
coordinate[4] = blockIdx.y / reg_dims[2] % reg_dims[3];
coordinate[5] = blockIdx.y / (reg_dims[2] * reg_dims[3]);
coordinate[6] = blockIdx.z % reg_dims[4];
coordinate[7] = blockIdx.z / reg_dims[4] % reg_dims[5];
coordinate[8] = blockIdx.z / (reg_dims[4] * reg_dims[5]);
break;
}
#pragma unroll
for (int dim = N - 1; dim >= 0; --dim) {
input_offset += coordinate[N - 1 - dim] * input_stride[dim];
}
out_data[output_offset] = input_data[input_offset];
}
}
template <typename T, typename Context>
bool LaunchStrided2ContiguousCaseOneKernel(
const Context& dev_ctx,
const T* input_data,
const Array<int64_t, DDim::kMaxRank + 1>& input_stride,
T* output_data,
const Array<int64_t, DDim::kMaxRank + 1>& dims,
int rank,
int64_t numel) {
dim3 grid(1, 1, 1), block(1, 1, 1);
Array<int64_t, 6> cur_dims;
block.x = 512;
if (rank >= 1) {
int64_t grid_x = (numel + static_cast<int64_t>(block.x) - 1) /
static_cast<int64_t>(block.x);
PADDLE_ENFORCE_LE_UINT32_MAX(grid_x, "strided_copy grid.x");
grid.x = static_cast<uint32_t>(grid_x);
cur_dims[0] = dims[rank - 1];
}
if (rank >= 2) {
cur_dims[1] = dims[rank - 2];
}
if (rank >= 4) {
int64_t grid_x = (dims[rank - 1] * dims[rank - 2] * dims[rank - 3] +
static_cast<int64_t>(block.x) - 1) /
static_cast<int64_t>(block.x);
PADDLE_ENFORCE_LE_UINT32_MAX(grid_x, "strided_copy grid.x");
grid.x = static_cast<uint32_t>(grid_x);
}
if (rank >= 6) {
int64_t grid_y = dims[rank - 4] * dims[rank - 5] * dims[rank - 6];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_y, "strided_copy grid.y");
grid.y = static_cast<uint32_t>(grid_y);
cur_dims[2] = dims[rank - 4];
cur_dims[3] = dims[rank - 5];
} else if (rank >= 5) {
int64_t grid_y = dims[rank - 4] * dims[rank - 5];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_y, "strided_copy grid.y");
grid.y = static_cast<uint32_t>(grid_y);
cur_dims[2] = dims[rank - 4];
cur_dims[3] = dims[rank - 5];
} else if (rank >= 4) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 4], "strided_copy grid.y");
grid.y = static_cast<uint32_t>(dims[rank - 4]);
cur_dims[2] = dims[rank - 4];
}
if (rank >= 9) {
int64_t grid_z = dims[rank - 7] * dims[rank - 8] * dims[rank - 9];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_z, "strided_copy grid.z");
grid.z = static_cast<uint32_t>(grid_z);
cur_dims[4] = dims[rank - 7];
cur_dims[5] = dims[rank - 8];
} else if (rank >= 8) {
int64_t grid_z = dims[rank - 7] * dims[rank - 8];
PADDLE_ENFORCE_LE_UINT32_MAX(grid_z, "strided_copy grid.z");
grid.z = static_cast<uint32_t>(grid_z);
cur_dims[4] = dims[rank - 7];
cur_dims[5] = dims[rank - 8];
} else if (rank >= 7) {
PADDLE_ENFORCE_LE_UINT32_MAX(dims[rank - 7], "strided_copy grid.z");
grid.z = static_cast<uint32_t>(dims[rank - 7]);
cur_dims[4] = dims[rank - 7];
}
if (!VerifyStridedCopyThreadConfigurationParameters(block, grid)) {
return false;
}
switch (rank) {
case 1:
Strided2ContiguousCaseOneFunc<T, 1><<<grid, block, 0, dev_ctx.stream()>>>(
input_data, input_stride, output_data, cur_dims, dims[rank - 1]);
break;
case 2:
Strided2ContiguousCaseOneFunc<T, 2><<<grid, block, 0, dev_ctx.stream()>>>(
input_data,
input_stride,
output_data,
cur_dims,
dims[rank - 1] * dims[rank - 2]);
break;
#define CASE_RANK(__Rk) \
case __Rk: \
Strided2ContiguousCaseOneFunc<T, __Rk> \
<<<grid, block, 0, dev_ctx.stream()>>>( \
input_data, \
input_stride, \
output_data, \
cur_dims, \
dims[rank - 1] * dims[rank - 2] * dims[rank - 3]); \
break
CASE_RANK(3);
CASE_RANK(4);
CASE_RANK(5);
CASE_RANK(6);
CASE_RANK(7);
CASE_RANK(8);
CASE_RANK(9);
#undef CASE_RANK
default:
PADDLE_THROW(common::errors::InvalidArgument(
"The rank of input should be less than 9, but received %d.", rank));
}
return true;
}
template <typename T, size_t IN_RANK>
__global__ void Strided2ContiguousDefaultFunc(
const T* input_data,
Array<int64_t, DDim::kMaxRank + 1> input_stride,
T* output_data,
Array<int64_t, DDim::kMaxRank + 1> dims,
const int64_t numel) {
int64_t gid =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
#pragma unroll
for (int64_t i = gid; i < numel; i += static_cast<int64_t>(blockDim.x) *
static_cast<int64_t>(gridDim.x)) {
int64_t input_offset = 0;
int64_t index_tmp = i;
#pragma unroll
for (int dim = IN_RANK - 1; dim >= 0; --dim) {
input_offset += (index_tmp % dims[dim]) * input_stride[dim];
index_tmp = index_tmp / dims[dim];
}
output_data[i] = input_data[input_offset];
}
}
template <typename T, typename Context>
void LaunchStrided2ContiguousDefaultKernel(
const Context& dev_ctx,
const T* input_data,
const Array<int64_t, DDim::kMaxRank + 1>& input_stride,
T* output_data,
const Array<int64_t, DDim::kMaxRank + 1>& dims,
int rank,
int64_t numel) {
constexpr uint32_t block = 512;
const int64_t grid_x = (numel + block - 1) / block;
PADDLE_ENFORCE_LE_UINT32_MAX(grid_x, "strided_copy grid.x");
const dim3 grid(static_cast<uint32_t>(grid_x));
const dim3 block_dim(block);
switch (rank) {
#define CASE_RANK(__Rk) \
case __Rk: \
Strided2ContiguousDefaultFunc<T, __Rk> \
<<<grid, block_dim, 0, dev_ctx.stream()>>>( \
input_data, input_stride, output_data, dims, numel); \
break
CASE_RANK(1);
CASE_RANK(2);
CASE_RANK(3);
CASE_RANK(4);
CASE_RANK(5);
CASE_RANK(6);
CASE_RANK(7);
CASE_RANK(8);
CASE_RANK(9);
#undef CASE_RANK
default:
PADDLE_THROW(common::errors::InvalidArgument(
"The rank of input should be less than 9, but received %d.", rank));
}
}
template <typename T, typename Context>
void StridedCopyKernel(const Context& dev_ctx,
const DenseTensor& input,
const std::vector<int64_t>& dims,
const std::vector<int64_t>& out_stride,
int64_t offset,
DenseTensor* out) {
DenseTensorMeta meta = input.meta();
meta.strides = make_ddim(out_stride);
meta.dims = make_ddim(dims);
meta.offset = offset;
out->set_meta(meta);
int rank = out->dims().size();
int64_t input_numel = input.numel();
int64_t output_numel = out->numel();
T* output_data = out->data<T>();
PADDLE_ENFORCE_NOT_NULL(output_data,
common::errors::InvalidArgument(
"StridedCopyKernel's out tensor must complete "
"mutable data before call kernel."));
Array<int64_t, DDim::kMaxRank + 1> output_dims;
Array<int64_t, DDim::kMaxRank + 1> output_stride;
for (int i = 0; i < meta.dims.size(); i++) {
output_dims[i] = meta.dims[i];
output_stride[i] = meta.strides[i];
}
const T* input_data = input.data<T>();
// count vecsize
int VecSize = 8;
VecSize = std::min(GetVectorizedSize<T>(input_data), VecSize);
VecSize = std::min(GetVectorizedSize<T>(output_data), VecSize);
while (VecSize > 1 && output_numel % VecSize != 0) {
VecSize /= 2;
}
if (input_numel != 1 && input_numel != output_numel) {
while (VecSize > 1 && input_numel % VecSize != 0) {
VecSize /= 2;
}
}
while (VecSize > 1 && output_dims[meta.dims.size() - 1] % VecSize != 0) {
VecSize /= 2;
}
if (output_stride[meta.dims.size() - 1] != 1) {
VecSize = 1;
}
if (input.dims() != out->dims()) {
if (input_numel == 1) {
switch (VecSize) {
#define CASE_VECSIZE(__Sz) \
case __Sz: \
StrideCopyDiffDimKernel<T, Context, __Sz>(dev_ctx, \
input_data, \
output_data, \
output_stride, \
output_dims, \
rank, \
input_numel, \
output_numel); \
break;
CASE_VECSIZE(1);
CASE_VECSIZE(2);
CASE_VECSIZE(4);
CASE_VECSIZE(8);
#undef CASE_VECSIZE
default:
PADDLE_THROW(common::errors::InvalidArgument(
"unsurport vecsize %d for StrideCopyDiffDimKernel", VecSize));
}
return;
} else {
bool can_expand = funcs::CheckIsLastDimsMatch(input.dims(), out->dims());
if (can_expand && input.meta().is_contiguous()) {
switch (VecSize) {
#define CASE_VECSIZE(__Sz) \
case __Sz: \
LaunchContiguous2StridedDefaultKernel<T, Context, __Sz>(dev_ctx, \
input_data, \
output_data, \
output_stride, \
output_dims, \
rank, \
input_numel, \
output_numel, \
false); \
break;
CASE_VECSIZE(1);
CASE_VECSIZE(2);
CASE_VECSIZE(4);
CASE_VECSIZE(8);
#undef CASE_VECSIZE
default:
PADDLE_THROW(common::errors::InvalidArgument(
"unsurport vecsize %d for "
"LaunchContiguous2StridedDefaultKernel",
VecSize));
}
return;
}
}
}
PADDLE_ENFORCE_EQ(input.dims(),
out->dims(),
common::errors::InvalidArgument(
"Input shape(%s) must be equal with out shape(%s).",
input.dims(),
out->dims()));
PADDLE_ENFORCE_EQ(input_numel,
output_numel,
common::errors::InvalidArgument(
"Input numel(%d) must be equal with out numel(%d).",
input_numel,
output_numel));
Array<int64_t, DDim::kMaxRank + 1> input_dims;
Array<int64_t, DDim::kMaxRank + 1> input_stride;
for (int i = 0; i < input.dims().size(); i++) {
input_dims[i] = input.dims()[i];
input_stride[i] = input.strides()[i];
}
if (output_numel == 1) {
#ifdef PADDLE_WITH_HIP
hipMemcpy(output_data,
input_data,
SizeOf(input.dtype()),
hipMemcpyDeviceToDevice);
#else
cudaMemcpy(output_data,
input_data,
SizeOf(input.dtype()),
cudaMemcpyDeviceToDevice);
#endif
return;
}
if (input.meta().is_contiguous()) {
if (LaunchContiguous2StridedCaseZeroKernel<T, Context>(dev_ctx,
input_data,
output_data,
output_stride,
output_dims,
rank,
false)) {
} else if (LaunchContiguous2StridedCaseOneKernel<T, Context>(dev_ctx,
input_data,
output_data,
output_stride,
output_dims,
rank,
output_numel,
false)) {
} else {
switch (VecSize) {
#define CASE_VECSIZE(__Sz) \
case __Sz: \
LaunchContiguous2StridedDefaultKernel<T, Context, __Sz>(dev_ctx, \
input_data, \
output_data, \
output_stride, \
output_dims, \
rank, \
input_numel, \
output_numel, \
false); \
break;
CASE_VECSIZE(1);
CASE_VECSIZE(2);
CASE_VECSIZE(4);
CASE_VECSIZE(8);
#undef CASE_VECSIZE
default:
PADDLE_THROW(common::errors::InvalidArgument(
"unsurport vecsize %d for StrideCopyKernel", VecSize));
}
}
} else if (out->meta().is_contiguous()) {
if (LaunchStrided2ContiguousCaseZeroKernel<T, Context>(
dev_ctx, input_data, input_stride, output_data, input_dims, rank)) {
} else if (LaunchStrided2ContiguousCaseOneKernel<T, Context>(
dev_ctx,
input_data,
input_stride,
output_data,
input_dims,
rank,
output_numel)) {
} else {
LaunchStrided2ContiguousDefaultKernel<T, Context>(dev_ctx,
input_data,
input_stride,
output_data,
input_dims,
rank,
output_numel);
}
} else {
if (LaunchStridedCopyCaseZeroKernel<T, Context>(dev_ctx,
input_data,
input_stride,
output_data,
output_stride,
input_dims,
rank)) {
} else if (LaunchStridedCopyCaseOneKernel<T, Context>(dev_ctx,
input_data,
input_stride,
output_data,
output_stride,
input_dims,
rank,
output_numel)) {
} else {
LaunchStridedCopyDefaultKernel<T, Context>(dev_ctx,
input_data,
input_stride,
output_data,
output_stride,
input_dims,
rank,
output_numel);
}
}
}
#ifdef _WIN32
INSTANTIATE_STRIDEDCOPY_KERNEL(bool, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(uint8_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(uint16_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(uint32_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(uint64_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(int8_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(int16_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(int32_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(int64_t, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(float, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(double, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(dtype::float16, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(dtype::bfloat16, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(dtype::complex<float>, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(dtype::complex<double>, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(dtype::float8_e4m3fn, GPUContext)
INSTANTIATE_STRIDEDCOPY_KERNEL(dtype::float8_e5m2, GPUContext)
#endif
} // namespace phi
PD_REGISTER_KERNEL(strided_copy,
GPU,
ALL_LAYOUT,
phi::StridedCopyKernel,
bool,
uint8_t,
uint16_t,
uint32_t,
uint64_t,
int8_t,
int16_t,
int32_t,
int64_t,
float,
double,
phi::float16,
phi::bfloat16,
phi::complex64,
phi::complex128,
phi::float8_e4m3fn,
phi::float8_e5m2) {}