Remove some D2D memcpy in T5 BeamSearch (#15706)

### Description
<!-- Describe your changes. -->

replace D2D memcpy with cuda kernels in creating decoder initial feeds
before:

![image](https://user-images.githubusercontent.com/52801275/234746326-209110e3-1cd4-4e8c-a488-515dd34c06c7.png)


after:

![image](https://user-images.githubusercontent.com/52801275/234736352-0bd027a0-e382-47d7-a1c5-ae9fbc9f1c9d.png)


### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->

---------

Co-authored-by: Ubuntu <wy@v100-2.0cdb2e52twzevn1i4fi45bylyg.jx.internal.cloudapp.net>
This commit is contained in:
Ye Wang 2023-05-01 13:03:21 -07:00 committed by GitHub
parent 0ffae8073b
commit 7f293d065a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 247 additions and 30 deletions

View file

@ -6,6 +6,7 @@
#include "cub/util_type.cuh"
#include <cub/cub.cuh>
#include <cub/device/device_segmented_radix_sort.cuh>
#include "contrib_ops/cuda/bert/utils.cuh"
#include "contrib_ops/cuda/transformers/generation_cuda_impl.h"
namespace onnxruntime {
@ -805,6 +806,206 @@ void UpdateDecoderMaskedMultiHeadAttentionCacheIndirection(int32_t* tgt_indir_ca
max_seq_length,
current_length);
}
#ifndef USE_ROCM
namespace {
template <typename T, size_t size>
struct TypeMapper : public V_vec_m_<T, size> {};
template <>
struct TypeMapper<int32_t, 2> {
using Type = uint2;
};
template <>
struct TypeMapper<int32_t, 4> {
using Type = uint4;
};
} // namespace
#endif
template <typename T>
__global__ void KeyCacheExpansionKernel(const T* input,
T* output,
int beam_width,
int max_seq_length,
int head_size) {
const int num_heads = gridDim.y;
const int sequence_length = gridDim.z;
const int bbid = blockIdx.x;
const int batch_id = bbid / beam_width;
const int head_id = blockIdx.y;
const int s = blockIdx.z;
const int tidx = threadIdx.x;
const int input_offset = ((batch_id * num_heads + head_id) * sequence_length + s) * head_size + tidx;
const int output_offset = ((bbid * num_heads + head_id) * max_seq_length + s) * head_size + tidx;
if (tidx < head_size) {
output[output_offset] = input[input_offset];
}
}
template <typename T>
void KeyCacheExpansionKernelLauncher(const T* key_cache,
T* key_cache_expanded,
int batch_size,
int beam_width,
int num_heads,
int sequence_length,
int max_seq_length,
int head_size,
cudaStream_t stream) {
const dim3 grid(batch_size * beam_width, num_heads, sequence_length);
int equiv_head_size = (head_size & 1) == 0 ? (head_size >> 1) : head_size;
equiv_head_size = (equiv_head_size & 1) == 0 ? (equiv_head_size >> 1) : equiv_head_size;
// Here we know head_size is smaller than max_thread_num_per_block
int tpb = std::max(32, equiv_head_size);
// round up tpb to power of 2
--tpb;
tpb |= (tpb >> 1);
tpb |= (tpb >> 2);
tpb |= (tpb >> 4);
tpb |= (tpb >> 8);
tpb |= (tpb >> 16);
tpb++;
#ifndef USE_ROCM
if ((head_size % 4) == 0) {
using vec_type = typename TypeMapper<T, 4>::Type;
const dim3 block(tpb);
KeyCacheExpansionKernel<<<grid, block, 0, stream>>>(reinterpret_cast<const vec_type*>(key_cache),
reinterpret_cast<vec_type*>(key_cache_expanded),
beam_width,
max_seq_length,
equiv_head_size);
} else if ((head_size & 1) == 0) {
using vec_type = typename TypeMapper<T, 2>::Type;
const dim3 block(tpb);
KeyCacheExpansionKernel<<<grid, block, 0, stream>>>(reinterpret_cast<const vec_type*>(key_cache),
reinterpret_cast<vec_type*>(key_cache_expanded),
beam_width,
max_seq_length,
equiv_head_size);
} else {
#endif
const dim3 block(tpb);
KeyCacheExpansionKernel<<<grid, block, 0, stream>>>(key_cache,
key_cache_expanded,
beam_width,
max_seq_length,
head_size);
#ifndef USE_ROCM
}
#endif
}
template void KeyCacheExpansionKernelLauncher(const float* key_cache,
float* key_cache_expanded,
int batch_size,
int beam_width,
int num_heads,
int sequence_length,
int max_seq_length,
int head_size,
cudaStream_t stream);
template void KeyCacheExpansionKernelLauncher(const half* key_cache,
half* key_cache_expanded,
int batch_size,
int beam_width,
int num_heads,
int sequence_length,
int max_seq_length,
int head_size,
cudaStream_t stream);
template void KeyCacheExpansionKernelLauncher(const int32_t* key_cache,
int32_t* key_cache_expanded,
int batch_size,
int beam_width,
int num_heads,
int sequence_length,
int max_seq_length,
int head_size,
cudaStream_t stream);
template <typename T>
__global__ void BufferExpansionKernel(const T* input,
T* output,
int chunk_size) {
const int batch_id = blockIdx.x;
const int beam_id = blockIdx.y;
const int tidx = threadIdx.x;
const int beam_size = gridDim.y;
const int idx = blockIdx.z * blockDim.x + tidx;
const int input_offset = batch_id * chunk_size + idx;
const int output_offset = batch_id * beam_size * chunk_size + beam_id * chunk_size + idx;
if (idx < chunk_size) {
output[output_offset] = input[input_offset];
}
}
template <typename T>
void BufferExpansionKernelLauncher(const T* input,
T* output,
int batch_size,
int beam_width,
int chunk_size,
cudaStream_t stream) {
const dim3 block(128);
#ifndef USE_ROCM
if ((chunk_size % 4) == 0) {
using vec_type = typename TypeMapper<T, 4>::Type;
const dim3 grid(batch_size, beam_width, (chunk_size / 4 + block.x - 1) / block.x);
BufferExpansionKernel<<<grid, block, 0, stream>>>(reinterpret_cast<const vec_type*>(input),
reinterpret_cast<vec_type*>(output),
chunk_size / 4);
} else if ((chunk_size & 1) == 0) {
using vec_type = typename TypeMapper<T, 2>::Type;
const dim3 grid(batch_size, beam_width, (chunk_size / 2 + block.x - 1) / block.x);
BufferExpansionKernel<<<grid, block, 0, stream>>>(reinterpret_cast<const vec_type*>(input),
reinterpret_cast<vec_type*>(output),
chunk_size / 2);
} else {
#endif
const dim3 grid(batch_size, beam_width, (chunk_size + block.x - 1) / block.x);
BufferExpansionKernel<<<grid, block, 0, stream>>>(input,
output,
chunk_size);
#ifndef USE_ROCM
}
#endif
}
template void BufferExpansionKernelLauncher(const float* input,
float* output,
int batch_size,
int beam_width,
int chunk_size,
cudaStream_t stream);
template void BufferExpansionKernelLauncher(const half* input,
half* output,
int batch_size,
int beam_width,
int chunk_size,
cudaStream_t stream);
template void BufferExpansionKernelLauncher(const int32_t* input,
int32_t* output,
int batch_size,
int beam_width,
int chunk_size,
cudaStream_t stream);
} // namespace cuda
} // namespace contrib
} // namespace onnxruntime

View file

@ -119,6 +119,25 @@ void UpdateDecoderMaskedMultiHeadAttentionCacheIndirection(int32_t* tgt_indir_ca
int current_length,
cudaStream_t stream);
template <typename T>
void KeyCacheExpansionKernelLauncher(const T* key_cache,
T* key_cache_expanded,
int batch_size,
int beam_width,
int num_heads,
int sequence_length,
int max_seq_length,
int head_size,
cudaStream_t stream);
template <typename T>
void BufferExpansionKernelLauncher(const T* input,
T* output,
int batch_size,
int beam_width,
int chunk_size,
cudaStream_t stream);
} // namespace cuda
} // namespace contrib
} // namespace onnxruntime

View file

@ -1152,6 +1152,16 @@ Status UpdateDecoderFeeds(
return Status::OK();
}
namespace {
template <typename T>
struct ToCudaTypeWrapper : public ToCudaType<T> {};
template <>
struct ToCudaTypeWrapper<int32_t> {
using MappedType = int32_t;
};
} // namespace
template <typename T>
Status ExpandBuffer(Stream* ort_stream,
const OrtValue& input,
@ -1188,23 +1198,18 @@ Status ExpandBuffer(Stream* ort_stream,
const T* input_data = input.Get<Tensor>().Data<T>();
T* expanded_data = expanded.GetMutable<Tensor>()->MutableData<T>();
T* target = expanded_data;
using CudaT = typename ToCudaTypeWrapper<T>::MappedType;
if (max_sequence_length == 0) {
const int64_t& chunk_size = static_cast<int64_t>(input_shape.Size() / batch_size);
for (int i = 0; i < batch_size; i++) {
for (int j = 0; j < num_beams; j++) {
CUDA_RETURN_IF_ERROR(
cudaMemcpyAsync(
target,
input_data + i * chunk_size,
sizeof(T) * chunk_size,
cudaMemcpyDeviceToDevice,
cuda_stream));
target += chunk_size;
}
}
cuda::BufferExpansionKernelLauncher<CudaT>(reinterpret_cast<const CudaT*>(input_data),
reinterpret_cast<CudaT*>(expanded_data),
static_cast<int>(batch_size),
num_beams,
static_cast<int>(chunk_size),
cuda_stream);
return Status::OK();
}
@ -1213,24 +1218,16 @@ Status ExpandBuffer(Stream* ort_stream,
// Expand from [B, N, S, H] to [B*beam, N, S_max, H]
const int64_t& num_heads = input_shape[1];
const int64_t& head_size = input_shape[3];
const int64_t& input_offset = sequence_length * head_size;
const int64_t& output_offset = max_sequence_length * head_size;
const int64_t& NSH = input_offset * num_heads;
for (int i = 0; i < batch_size; i++) {
for (int j = 0; j < num_beams; j++) {
for (int k = 0; k < num_heads; k++) {
CUDA_RETURN_IF_ERROR(
cudaMemcpyAsync(
target,
input_data + i * NSH + k * input_offset,
sizeof(T) * SafeInt<size_t>(input_offset),
cudaMemcpyDeviceToDevice,
cuda_stream));
target += output_offset;
}
}
}
cuda::KeyCacheExpansionKernelLauncher<CudaT>(reinterpret_cast<const CudaT*>(input_data),
reinterpret_cast<CudaT*>(expanded_data),
static_cast<int>(batch_size),
num_beams,
static_cast<int>(num_heads),
static_cast<int>(sequence_length),
max_sequence_length,
static_cast<int>(head_size),
cuda_stream);
return Status::OK();
}