mirror of
https://github.com/hanzoai/musetalk-bench.git
synced 2026-11-03 01:49:22 +00:00
345 lines
14 KiB
Plaintext
345 lines
14 KiB
Plaintext
// High-occupancy direct 3x3 convolution kernels for the TAESD tiny-encoder/decoder.
|
|
// Targets the low-channel-count (C<=64) high-resolution convs (256/128/64 spatial) where cuDNN's
|
|
// implicit-GEMM underfills the tensor-core MMA tiles and runs at a small fraction of peak. A direct
|
|
// SIMT kernel that stages the input tile in shared memory (loaded once, reused 9*C_out times) and
|
|
// streams the small weight tensor from L2 reaches far higher occupancy on these shapes.
|
|
//
|
|
// Layout: NCHW in/out, f16 storage with f32 accumulation. 3x3 kernel, pad 1, stride 1 or 2.
|
|
// Optional per-channel bias and optional fused ReLU (TAESD blocks are conv->relu chains).
|
|
// Included at the end of conv.cu, which already pulls in cuda_utils.cuh / stdint.
|
|
|
|
#ifndef TAESD_TH
|
|
#define TAESD_TH 8
|
|
#endif
|
|
#ifndef TAESD_TW
|
|
#define TAESD_TW 16
|
|
#endif
|
|
|
|
// One block computes a TH x TW output tile for ALL C_out channels of one batch element.
|
|
// blockDim = (TW, TH). Shared memory holds the input receptive field for the tile across all C_in.
|
|
// Each thread owns one output pixel and loops over output channels, accumulating in f32.
|
|
template <int TH, int TW>
|
|
__device__ __forceinline__ void taesd_conv3x3_impl(
|
|
const __half *__restrict__ src, // (N, C_in, H, W)
|
|
const __half *__restrict__ weight, // (C_out, C_in, 3, 3)
|
|
const __half *__restrict__ bias, // (C_out); valid only when do_bias
|
|
__half *__restrict__ dst, // (N, C_out, H_out, W_out)
|
|
const int N, const int C_in, const int C_out,
|
|
const int H, const int W, const int H_out, const int W_out,
|
|
const int stride, const int do_relu, const int do_bias)
|
|
{
|
|
extern __shared__ __half smem[]; // (C_in, halo_h, halo_w)
|
|
const int halo_h = TH * stride + 2;
|
|
const int halo_w = TW * stride + 2;
|
|
const int tile_oh = blockIdx.y * TH;
|
|
const int tile_ow = blockIdx.x * TW;
|
|
const int n = blockIdx.z;
|
|
|
|
// Top-left input coord of this tile's receptive field (stride*out - pad), pad=1.
|
|
const int in_h0 = tile_oh * stride - 1;
|
|
const int in_w0 = tile_ow * stride - 1;
|
|
|
|
const int tx = threadIdx.x; // 0..TW-1
|
|
const int ty = threadIdx.y; // 0..TH-1
|
|
const int tid = ty * TW + tx;
|
|
const int nthreads = TW * TH;
|
|
const int halo_hw = halo_h * halo_w;
|
|
|
|
// Cooperative load of the input halo into shared memory, zero-padded at the image border.
|
|
const __half zero = __float2half(0.0f);
|
|
for (int c = 0; c < C_in; ++c) {
|
|
const __half *src_c = src + ((size_t)(n * C_in + c)) * H * W;
|
|
__half *sm_c = smem + (size_t)c * halo_hw;
|
|
for (int idx = tid; idx < halo_hw; idx += nthreads) {
|
|
const int hh = idx / halo_w;
|
|
const int ww = idx % halo_w;
|
|
const int ih = in_h0 + hh;
|
|
const int iw = in_w0 + ww;
|
|
__half v = zero;
|
|
if (ih >= 0 && ih < H && iw >= 0 && iw < W) {
|
|
v = src_c[ih * W + iw];
|
|
}
|
|
sm_c[idx] = v;
|
|
}
|
|
}
|
|
__syncthreads();
|
|
|
|
const int oh = tile_oh + ty;
|
|
const int ow = tile_ow + tx;
|
|
const int active = (oh < H_out && ow < W_out);
|
|
|
|
// This thread's input window top-left within shared memory (stride within tile).
|
|
const int sh0 = ty * stride;
|
|
const int sw0 = tx * stride;
|
|
|
|
for (int oc = 0; oc < C_out; ++oc) {
|
|
float acc = do_bias ? __half2float(bias[oc]) : 0.0f;
|
|
const __half *w_oc = weight + (size_t)oc * C_in * 9;
|
|
if (active) {
|
|
for (int c = 0; c < C_in; ++c) {
|
|
const __half *sm_c = smem + (size_t)c * halo_hw;
|
|
const __half *w_c = w_oc + c * 9;
|
|
#pragma unroll
|
|
for (int ky = 0; ky < 3; ++ky) {
|
|
const __half *sm_row = sm_c + (sh0 + ky) * halo_w + sw0;
|
|
#pragma unroll
|
|
for (int kx = 0; kx < 3; ++kx) {
|
|
acc += __half2float(sm_row[kx]) * __half2float(w_c[ky * 3 + kx]);
|
|
}
|
|
}
|
|
}
|
|
if (do_relu && acc < 0.0f) acc = 0.0f;
|
|
dst[(((size_t)(n * C_out + oc)) * H_out + oh) * W_out + ow] = __float2half(acc);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" __global__ void taesd_conv3x3_f16(
|
|
const __half *src, const __half *weight, const __half *bias, __half *dst,
|
|
const int N, const int C_in, const int C_out,
|
|
const int H, const int W, const int H_out, const int W_out,
|
|
const int stride, const int do_relu, const int do_bias)
|
|
{
|
|
taesd_conv3x3_impl<TAESD_TH, TAESD_TW>(
|
|
src, weight, bias, dst, N, C_in, C_out, H, W, H_out, W_out, stride, do_relu, do_bias);
|
|
}
|
|
|
|
// Channel-register-blocked variant: each thread accumulates OCB output channels at once so each
|
|
// shared-memory input read is reused across OCB weights, raising arithmetic intensity and cutting
|
|
// shared-load traffic by OCB. Best for the dominant C_out=64 stride-1 convs.
|
|
template <int TH, int TW, int OCB>
|
|
__device__ __forceinline__ void taesd_conv3x3_ocb_impl(
|
|
const __half *__restrict__ src,
|
|
const __half *__restrict__ weight,
|
|
const __half *__restrict__ bias,
|
|
__half *__restrict__ dst,
|
|
const int N, const int C_in, const int C_out,
|
|
const int H, const int W, const int H_out, const int W_out,
|
|
const int stride, const int do_relu, const int do_bias)
|
|
{
|
|
extern __shared__ __half smem[];
|
|
const int halo_h = TH * stride + 2;
|
|
const int halo_w = TW * stride + 2;
|
|
const int tile_oh = blockIdx.y * TH;
|
|
const int tile_ow = blockIdx.x * TW;
|
|
const int n = blockIdx.z;
|
|
const int in_h0 = tile_oh * stride - 1;
|
|
const int in_w0 = tile_ow * stride - 1;
|
|
const int tx = threadIdx.x;
|
|
const int ty = threadIdx.y;
|
|
const int tid = ty * TW + tx;
|
|
const int nthreads = TW * TH;
|
|
const int halo_hw = halo_h * halo_w;
|
|
|
|
const __half zero = __float2half(0.0f);
|
|
for (int c = 0; c < C_in; ++c) {
|
|
const __half *src_c = src + ((size_t)(n * C_in + c)) * H * W;
|
|
__half *sm_c = smem + (size_t)c * halo_hw;
|
|
for (int idx = tid; idx < halo_hw; idx += nthreads) {
|
|
const int hh = idx / halo_w;
|
|
const int ww = idx % halo_w;
|
|
const int ih = in_h0 + hh;
|
|
const int iw = in_w0 + ww;
|
|
__half v = zero;
|
|
if (ih >= 0 && ih < H && iw >= 0 && iw < W) v = src_c[ih * W + iw];
|
|
sm_c[idx] = v;
|
|
}
|
|
}
|
|
__syncthreads();
|
|
|
|
const int oh = tile_oh + ty;
|
|
const int ow = tile_ow + tx;
|
|
const int active = (oh < H_out && ow < W_out);
|
|
const int sh0 = ty * stride;
|
|
const int sw0 = tx * stride;
|
|
|
|
for (int oc0 = 0; oc0 < C_out; oc0 += OCB) {
|
|
float acc[OCB];
|
|
#pragma unroll
|
|
for (int j = 0; j < OCB; ++j) acc[j] = (do_bias && oc0 + j < C_out) ? __half2float(bias[oc0 + j]) : 0.0f;
|
|
if (active) {
|
|
for (int c = 0; c < C_in; ++c) {
|
|
const __half *sm_c = smem + (size_t)c * halo_hw;
|
|
#pragma unroll
|
|
for (int ky = 0; ky < 3; ++ky) {
|
|
const __half *sm_row = sm_c + (sh0 + ky) * halo_w + sw0;
|
|
#pragma unroll
|
|
for (int kx = 0; kx < 3; ++kx) {
|
|
const float sv = __half2float(sm_row[kx]);
|
|
#pragma unroll
|
|
for (int j = 0; j < OCB; ++j) {
|
|
const __half *w_oc = weight + (size_t)(oc0 + j) * C_in * 9;
|
|
acc[j] += sv * __half2float(w_oc[c * 9 + ky * 3 + kx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#pragma unroll
|
|
for (int j = 0; j < OCB; ++j) {
|
|
if (oc0 + j < C_out) {
|
|
float a = acc[j];
|
|
if (do_relu && a < 0.0f) a = 0.0f;
|
|
dst[(((size_t)(n * C_out + oc0 + j)) * H_out + oh) * W_out + ow] = __float2half(a);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" __global__ void taesd_conv3x3_ocb4_f16(
|
|
const __half *src, const __half *weight, const __half *bias, __half *dst,
|
|
const int N, const int C_in, const int C_out,
|
|
const int H, const int W, const int H_out, const int W_out,
|
|
const int stride, const int do_relu, const int do_bias)
|
|
{
|
|
taesd_conv3x3_ocb_impl<TAESD_TH, TAESD_TW, 4>(
|
|
src, weight, bias, dst, N, C_in, C_out, H, W, H_out, W_out, stride, do_relu, do_bias);
|
|
}
|
|
// v2: register-blocked direct 3x3 conv for stride-1 64-channel TAESD blocks. Each thread owns a
|
|
// PH x PW micro-tile of output pixels for a slab of OCB output channels, accumulating in registers.
|
|
// The input halo is staged in shared memory once; the weight slab for the current output-channel
|
|
// block is also staged in shared memory and reused across the whole pixel tile. This raises
|
|
// arithmetic intensity (each shared load feeds PH*PW*OCB FMAs) and reuses weights from shared
|
|
// instead of re-streaming them from L2 per pixel as v1 did.
|
|
//
|
|
// Block computes a (TPH*PH) x (TPW*PW) output tile for all C_out channels of one batch element.
|
|
// blockDim = (TPW, TPH). Stride-1, pad-1, 3x3 only. f16 in/out, f32 accumulate, fused bias+relu.
|
|
|
|
#ifndef TAESD_TPH
|
|
#define TAESD_TPH 8
|
|
#endif
|
|
#ifndef TAESD_TPW
|
|
#define TAESD_TPW 8
|
|
#endif
|
|
#ifndef TAESD_PH
|
|
#define TAESD_PH 2
|
|
#endif
|
|
#ifndef TAESD_PW
|
|
#define TAESD_PW 2
|
|
#endif
|
|
|
|
template <int TPH, int TPW, int PH, int PW, int OCB>
|
|
__device__ __forceinline__ void taesd_conv3x3_reg_impl(
|
|
const __half *__restrict__ src,
|
|
const __half *__restrict__ weight,
|
|
const __half *__restrict__ bias,
|
|
__half *__restrict__ dst,
|
|
const int N, const int C_in, const int C_out,
|
|
const int H, const int W, const int H_out, const int W_out,
|
|
const int do_relu, const int do_bias)
|
|
{
|
|
constexpr int TH = TPH * PH; // output tile height in pixels
|
|
constexpr int TW = TPW * PW; // output tile width in pixels
|
|
const int halo_h = TH + 2;
|
|
const int halo_w = TW + 2;
|
|
const int halo_hw = halo_h * halo_w;
|
|
|
|
extern __shared__ __half smem[];
|
|
__half *sm_in = smem; // (C_in, halo_h, halo_w)
|
|
__half *sm_w = smem + (size_t)C_in * halo_hw; // (OCB, C_in, 3, 3) for the current oc block
|
|
|
|
const int tile_oh = blockIdx.y * TH;
|
|
const int tile_ow = blockIdx.x * TW;
|
|
const int n = blockIdx.z;
|
|
const int in_h0 = tile_oh - 1;
|
|
const int in_w0 = tile_ow - 1;
|
|
const int tx = threadIdx.x;
|
|
const int ty = threadIdx.y;
|
|
const int tid = ty * TPW + tx;
|
|
const int nthreads = TPW * TPH;
|
|
|
|
// Stage the full input halo for this tile across all C_in.
|
|
const __half zero = __float2half(0.0f);
|
|
for (int c = 0; c < C_in; ++c) {
|
|
const __half *src_c = src + ((size_t)(n * C_in + c)) * H * W;
|
|
__half *smc = sm_in + (size_t)c * halo_hw;
|
|
for (int idx = tid; idx < halo_hw; idx += nthreads) {
|
|
const int hh = idx / halo_w;
|
|
const int ww = idx % halo_w;
|
|
const int ih = in_h0 + hh;
|
|
const int iw = in_w0 + ww;
|
|
smc[idx] = (ih >= 0 && ih < H && iw >= 0 && iw < W) ? src_c[ih * W + iw] : zero;
|
|
}
|
|
}
|
|
|
|
// This thread's top-left output pixel in the tile.
|
|
const int p_oh = ty * PH;
|
|
const int p_ow = tx * PW;
|
|
|
|
for (int oc0 = 0; oc0 < C_out; oc0 += OCB) {
|
|
const int ocn = min(OCB, C_out - oc0);
|
|
// Cooperatively stage the weight slab for [oc0, oc0+ocn) into shared memory.
|
|
const int wcount = ocn * C_in * 9;
|
|
for (int idx = tid; idx < wcount; idx += nthreads) {
|
|
sm_w[idx] = weight[(size_t)oc0 * C_in * 9 + idx];
|
|
}
|
|
__syncthreads();
|
|
|
|
float acc[OCB][PH][PW];
|
|
#pragma unroll
|
|
for (int o = 0; o < OCB; ++o)
|
|
#pragma unroll
|
|
for (int i = 0; i < PH; ++i)
|
|
#pragma unroll
|
|
for (int j = 0; j < PW; ++j)
|
|
acc[o][i][j] = (do_bias && oc0 + o < C_out) ? __half2float(bias[oc0 + o]) : 0.0f;
|
|
|
|
for (int c = 0; c < C_in; ++c) {
|
|
const __half *smc = sm_in + (size_t)c * halo_hw;
|
|
const __half *smwc = sm_w + (size_t)c * 9; // weight for channel c, base; oc stride = C_in*9
|
|
#pragma unroll
|
|
for (int ky = 0; ky < 3; ++ky) {
|
|
#pragma unroll
|
|
for (int kx = 0; kx < 3; ++kx) {
|
|
// Load weights for this (c,ky,kx) across the OCB output channels.
|
|
float wv[OCB];
|
|
#pragma unroll
|
|
for (int o = 0; o < OCB; ++o)
|
|
wv[o] = __half2float(smwc[(size_t)o * C_in * 9 + ky * 3 + kx]);
|
|
// Load the PH*PW input window samples once, reuse across OCB.
|
|
#pragma unroll
|
|
for (int i = 0; i < PH; ++i) {
|
|
const int srow = (p_oh + i + ky) * halo_w + (p_ow + kx);
|
|
#pragma unroll
|
|
for (int j = 0; j < PW; ++j) {
|
|
const float sv = __half2float(smc[srow + j]);
|
|
#pragma unroll
|
|
for (int o = 0; o < OCB; ++o)
|
|
acc[o][i][j] += sv * wv[o];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Write back.
|
|
#pragma unroll
|
|
for (int o = 0; o < OCB; ++o) {
|
|
if (oc0 + o >= C_out) continue;
|
|
#pragma unroll
|
|
for (int i = 0; i < PH; ++i) {
|
|
const int oh = tile_oh + p_oh + i;
|
|
if (oh >= H_out) continue;
|
|
#pragma unroll
|
|
for (int j = 0; j < PW; ++j) {
|
|
const int ow = tile_ow + p_ow + j;
|
|
if (ow >= W_out) continue;
|
|
float a = acc[o][i][j];
|
|
if (do_relu && a < 0.0f) a = 0.0f;
|
|
dst[(((size_t)(n * C_out + oc0 + o)) * H_out + oh) * W_out + ow] = __float2half(a);
|
|
}
|
|
}
|
|
}
|
|
__syncthreads();
|
|
}
|
|
}
|
|
|
|
extern "C" __global__ void taesd_conv3x3_reg_f16(
|
|
const __half *src, const __half *weight, const __half *bias, __half *dst,
|
|
const int N, const int C_in, const int C_out,
|
|
const int H, const int W, const int H_out, const int W_out,
|
|
const int do_relu, const int do_bias)
|
|
{
|
|
taesd_conv3x3_reg_impl<TAESD_TPH, TAESD_TPW, TAESD_PH, TAESD_PW, 8>(
|
|
src, weight, bias, dst, N, C_in, C_out, H, W, H_out, W_out, do_relu, do_bias);
|
|
}
|