Files
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

308 lines
11 KiB
Plaintext

/*
* SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION &
* AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0
*
* 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 <cuda.h>
#if CUDA_VERSION >= 10010
#include "NvInfer.h"
#include "common/bertCommon.h"
#include "common/common.cuh"
#include "common/serialize.hpp"
#include "skipLayerNormPlugin.h"
#include "skipLayerNormPluginLegacy.h"
#include <cassert>
#include <cstring>
#include <limits>
#include <vector>
using namespace nvinfer1;
using namespace nvinfer1::plugin;
namespace nvinfer1
{
namespace plugin
{
namespace bert
{
template <int32_t TPB, int32_t VPT, bool hasBias>
__global__ void skiplnDQQ(int32_t const ld, int8_t const* input, int8_t const* skip, int8_t* output, __half const* beta,
__half const* gamma, __half const* bias, float const dqScaleIn, float const dqScaleSkip, float const qScale)
{
int32_t const idx = ld * blockIdx.x + threadIdx.x * VPT;
// 4 * 1024 * 4 * 2 Bytes = 16KB per block
int8_t inLocal[VPT];
int8_t skipLocal[VPT];
__half inLocalDQ[VPT]; // dequantized input + skip + bias
__half biasLocal[VPT]; // bias and beta
__half gammaLocal[VPT];
copy<sizeof(int8_t) * VPT>(&input[idx], inLocal);
copy<sizeof(int8_t) * VPT>(&skip[idx], skipLocal);
copy<sizeof(__half) * VPT>(&bias[threadIdx.x * VPT], biasLocal);
__half2 loc = __floats2half2_rn(0.f, 0.f); // accumulator
const __half rld = __half(1) / __half(ld);
#pragma unroll
for (int32_t it = 0; it < VPT; it++)
{
// DQ input and skip
float const tmpIn = inLocal[it];
float const tmpSkip = skipLocal[it];
inLocalDQ[it] = dqScaleIn * tmpIn + dqScaleSkip * tmpSkip;
if (hasBias)
inLocalDQ[it] += biasLocal[it];
const __half tmp = rld * inLocalDQ[it];
const __half2 tmp2 = __halves2half2(tmp, tmp * inLocalDQ[it]);
loc = loc + tmp2;
}
// load parameters
copy<sizeof(__half) * VPT>(&beta[threadIdx.x * VPT], biasLocal);
copy<sizeof(__half) * VPT>(&gamma[threadIdx.x * VPT], gammaLocal);
using BlockReduce = cub::BlockReduce<__half2, TPB>;
__shared__ typename BlockReduce::TempStorage tempStorage;
__shared__ __half mu; // mean
__shared__ __half rsigma; // 1 / std.dev.
const __half2 sum2 = BlockReduce(tempStorage).Reduce(loc, [](auto const& lhs, auto const& rhs){return lhs + rhs;});
if (threadIdx.x == 0)
{
mu = __low2half(sum2);
rsigma = rsqrt(__high2half(sum2) - mu * mu + std::numeric_limits<half>::epsilon());
}
__syncthreads();
static_assert(VPT % 4 == 0, "");
uint32_t outLocal[VPT / 4U];
#pragma unroll
for (int32_t it = 0; it < VPT / 4U; it++)
{
float const tmp0 = gammaLocal[it * 4 + 0] * (inLocalDQ[it * 4 + 0] - mu) * rsigma + biasLocal[it * 4 + 0];
float const tmp1 = gammaLocal[it * 4 + 1] * (inLocalDQ[it * 4 + 1] - mu) * rsigma + biasLocal[it * 4 + 1];
float const tmp2 = gammaLocal[it * 4 + 2] * (inLocalDQ[it * 4 + 2] - mu) * rsigma + biasLocal[it * 4 + 2];
float const tmp3 = gammaLocal[it * 4 + 3] * (inLocalDQ[it * 4 + 3] - mu) * rsigma + biasLocal[it * 4 + 3];
outLocal[it] = float4_to_char4(tmp0 * qScale, tmp1 * qScale, tmp2 * qScale, tmp3 * qScale);
}
copy<sizeof(int8_t) * VPT>(outLocal, &output[idx]);
}
template <typename T, int32_t TPB, int32_t VPT, bool hasBias>
__global__ void skipln_vec(
int32_t const ld, const T* input, const T* skip, T* output, const T* beta, const T* gamma, const T* bias)
{
int32_t const idx = ld * blockIdx.x + threadIdx.x * VPT;
// 4 * 1024 * 4 * 2 Bytes = 16KB per block
T inLocal[VPT];
T skipLocal[VPT];
T biasLocal[VPT];
// T gammaLocal[VPT];
copy<sizeof(T) * VPT>(&input[idx], inLocal);
copy<sizeof(T) * VPT>(&skip[idx], skipLocal);
copy<sizeof(T) * VPT>(&bias[threadIdx.x * VPT], biasLocal);
T local = 0.f;
T local2 = 0.f;
const T rld = T(1) / T(ld);
#pragma unroll
for (int32_t it = 0; it < VPT; it++)
{
inLocal[it] += skipLocal[it];
if (hasBias)
inLocal[it] += biasLocal[it];
const T tmp = rld * inLocal[it];
local += tmp;
local2 += tmp * inLocal[it];
}
copy<sizeof(T) * VPT>(&beta[threadIdx.x * VPT], biasLocal);
copy<sizeof(T) * VPT>(&gamma[threadIdx.x * VPT], skipLocal);
using BlockReduce = cub::BlockReduce<kvp<T>, TPB>;
__shared__ typename BlockReduce::TempStorage tempStorage;
__shared__ T mu; // mean
__shared__ T rsigma; // 1 / std.dev.
auto const sumKV = BlockReduce(tempStorage).Reduce(kvp<T>(local, local2), [](auto const& lhs, auto const& rhs){return lhs + rhs;});
if (threadIdx.x == 0)
{
mu = sumKV.key;
rsigma = rsqrt(sumKV.value - mu * mu + std::numeric_limits<T>::epsilon());
}
__syncthreads();
///*
#pragma unroll
for (int32_t it = 0; it < VPT; it++)
{
inLocal[it] = skipLocal[it] * (inLocal[it] - mu) * rsigma + biasLocal[it];
}
/* */
copy<sizeof(T) * VPT>(inLocal, &output[idx]);
}
template <typename T, unsigned TPB, bool hasBias>
__global__ void skipLayerNormKernelSmall(
int32_t const ld, const T* input, const T* skip, const T* beta, const T* gamma, T* output, const T* bias)
{
const T rld = T(1) / T(ld);
int32_t const offset = blockIdx.x * ld;
// reduce x and x^2
kvp<T> threadData(0, 0);
int32_t const idx = offset + threadIdx.x;
T val = 0;
if (threadIdx.x < ld)
{
val = input[idx] + skip[idx];
if (hasBias)
{
val += bias[threadIdx.x];
}
const T rldval = rld * val;
threadData = threadData + kvp<T>(rldval, rldval * val);
}
layerNormSmall<T, T, TPB>(val, threadData, ld, idx, beta, gamma, output);
}
template <typename T, unsigned TPB, bool hasBias>
__global__ void skipLayerNormKernel(
int32_t const ld, const T* input, const T* skip, const T* beta, const T* gamma, T* output, const T* bias)
{
const T rld = T(1) / T(ld);
int32_t const offset = blockIdx.x * ld;
// reduce x and x^2
kvp<T> threadData(0, 0);
for (int32_t i = threadIdx.x; i < ld; i += TPB)
{
int32_t const idx = offset + i;
T val = T(input[idx]) + T(skip[idx]);
if (hasBias)
{
val += T(bias[i]);
}
const T rldval = rld * val;
threadData = threadData + kvp<T>(rldval, rldval * val);
output[idx] = val;
}
layerNorm<T, T, T, TPB>(threadData, ld, offset, beta, gamma, output);
}
template <bool hasBias>
int32_t computeSkipLayerNormDQQ(cudaStream_t stream, int32_t const ld, int32_t const n, int8_t const* input,
int8_t const* skip, __half const* beta, __half const* gamma, int8_t* output, __half const* bias,
float const dqScaleIn, float const dqScaleSkip, float const qScale)
{
// this must be true because n is the total size of the tensor
PLUGIN_VALIDATE(n % ld == 0);
int32_t const gridSize = n / ld;
// we're limited by the size of the parameters, i.e. 8-wide instead of 16
constexpr int32_t VPT = 16 / sizeof(__half);
if (ld == 768)
{
constexpr int32_t TPB = 768 / VPT;
skiplnDQQ<TPB, VPT, hasBias>
<<<gridSize, TPB, 0, stream>>>(ld, input, skip, output, beta, gamma, bias, dqScaleIn, dqScaleSkip, qScale);
}
else if (ld == 1024)
{
constexpr int32_t TPB = 1024 / VPT;
skiplnDQQ<TPB, VPT, hasBias>
<<<gridSize, TPB, 0, stream>>>(ld, input, skip, output, beta, gamma, bias, dqScaleIn, dqScaleSkip, qScale);
}
else
{
// TODO need to implement this
PLUGIN_ERROR(("SkipLayerNormDQQ - FATAL: unsupported hidden layer size: " + std::to_string(ld)).c_str());
}
PLUGIN_CHECK(cudaPeekAtLastError());
return 0;
}
template <typename T, bool hasBias>
int32_t computeSkipLayerNorm(cudaStream_t stream, int32_t const ld, int32_t const n, const T* input, const T* skip,
const T* beta, const T* gamma, T* output, const T* bias)
{
// this must be true because n is the total size of the tensor
PLUGIN_VALIDATE(n % ld == 0);
int32_t const gridSize = n / ld;
constexpr int32_t VPT = 16 / sizeof(T);
if (ld <= 32)
{
constexpr int32_t blockSize = 32;
skipLayerNormKernelSmall<T, blockSize, hasBias>
<<<gridSize, blockSize, 0, stream>>>(ld, input, skip, beta, gamma, output, bias);
}
else if (ld == 768)
{
constexpr int32_t TPB = 768 / VPT;
skipln_vec<T, TPB, VPT, hasBias><<<gridSize, TPB, 0, stream>>>(ld, input, skip, output, beta, gamma, bias);
}
else if (ld == 1024)
{
constexpr int32_t TPB = 1024 / VPT;
skipln_vec<T, TPB, VPT, hasBias><<<gridSize, TPB, 0, stream>>>(ld, input, skip, output, beta, gamma, bias);
}
else
{
constexpr int32_t blockSize = 256;
skipLayerNormKernel<T, blockSize, hasBias>
<<<gridSize, blockSize, 0, stream>>>(ld, input, skip, beta, gamma, output, bias);
}
PLUGIN_CHECK(cudaPeekAtLastError());
return 0;
}
template int32_t computeSkipLayerNormDQQ<true>(cudaStream_t stream, int32_t const ld, int32_t const n,
int8_t const* input, int8_t const* skip, __half const* beta, __half const* gamma, int8_t* output,
__half const* bias, float const dqScaleIn, float const dqScaleSkip, float const qScale);
template int32_t computeSkipLayerNormDQQ<false>(cudaStream_t stream, int32_t const ld, int32_t const n,
int8_t const* input, int8_t const* skip, __half const* beta, __half const* gamma, int8_t* output,
__half const* bias, float const dqScaleIn, float const dqScaleSkip, float const qScale);
template int32_t computeSkipLayerNorm<float, true>(cudaStream_t, int32_t const, int32_t const, float const*,
float const*, float const*, float const*, float*, float const*);
template int32_t computeSkipLayerNorm<float, false>(cudaStream_t, int32_t const, int32_t const, float const*,
float const*, float const*, float const*, float*, float const*);
template int32_t computeSkipLayerNorm<half, true>(
cudaStream_t, int32_t const, int32_t const, half const*, half const*, half const*, half const*, half*, half const*);
template int32_t computeSkipLayerNorm<half, false>(
cudaStream_t, int32_t const, int32_t const, half const*, half const*, half const*, half const*, half*, half const*);
} // namespace bert
} // namespace plugin
} // namespace nvinfer1
#endif // CUDA_VERSION >= 10010