src/cwise_linear_op_gpu.cu (213 lines of code) (raw):
#if GOOGLE_CUDA
#include "ew_op_gpu.h"
// y = a*x + b or a*(x + b)
template <typename T>
__global__ void cwise_linear_axpb_forward(
T* Y,
const T* __restrict__ X,
const float* __restrict__ A,
const float* __restrict__ B,
uint CDHW, uint DHW, int bA, int bB, int relu, int swap)
{
uint tid = threadIdx.x;
uint c = blockIdx.x;
uint n = blockIdx.y;
uint offset = n * CDHW + c * DHW + tid;
float a = bA ? A[c] : 1.0f;
float b = bB ? B[c] : 0.0f;
#pragma unroll 1
for (uint i = tid; i < DHW; i += blockDim.x, offset += blockDim.x)
{
float x = load(add_ptr_u(X, offset));
float y = swap ? a*(x + b) : a*x + b;
if (relu)
y = ew_relu(y);
store(add_ptr_u(Y, offset), y);
}
}
// y = a*x + b
// dx = dy * a
// da = sum(dy * x)
// db = sum(dy)
// y = a*(x + b)
// dx = dy * a
// da = sum(dy*(x + b))
// db = sum(dy * a)
template <typename T>
__global__ void cwise_linear_axpb_backward(
T* DX,
float* DA,
float* DB,
const T* __restrict__ DY,
const T* __restrict__ X,
const float* __restrict__ A,
const float* __restrict__ B,
uint CDHW, uint NDHW, uint DHW, int bDB, int relu, int swap)
{
__shared__ float2 Share[32];
uint tid = threadIdx.x;
if (blockDim.x > 32)
{
float2 zero = {0.0f, 0.0f};
if (tid < 32)
Share[tid] = zero;
__syncthreads();
}
uint c = blockIdx.x;
uint offsetC = c * DHW;
float a = A[c];
float b = (relu || swap) && bDB ? B[c] : 0.0f;
float da = 0.0f, db = 0.0f;
#pragma unroll 1
for (int ndhw = tid; ndhw < NDHW; ndhw += blockDim.x)
{
uint n = ndhw / DHW;
uint dhw = ndhw % DHW;
uint offset = offsetC + n * CDHW + dhw;
float dy = load(add_ptr_u(DY, offset));
float x = load(add_ptr_u( X, offset));
if (relu)
dy = ew_relu_grad(dy, swap ? a*(x + b) : a*x + b);
float dx = dy * a;
da += swap ? dy * (x + b) : dy * x;
db += swap ? dx : dy;
store(add_ptr_u(DX, offset), dx);
}
float2 stats = {da, db};
// reduce within warp
for (int i = 16; i > 0; i >>= 1)
stats = ew_warp_sum(stats, i);
if (blockDim.x > 32)
{
// first thread of each warp store to shared
if ((tid & 31) == 0)
Share[tid/32] = stats;
__syncthreads();
if (tid < 32)
{
// first warp loads all prior reductions
stats = Share[tid];
// reduce within this last warp
#pragma unroll 1
for (int i = blockDim.x/64; i > 0; i >>= 1)
stats = ew_warp_sum(stats, i);
}
}
// single thread outputs final reductions
if (tid == 0)
{
DA[c] = stats.x;
if (bDB)
DB[c] = stats.y;
}
}
// db = sum(dy)
template <typename T>
__global__ void cwise_linear_xpb_backward(
T* DX,
float* DB,
const T* __restrict__ DY,
const T* __restrict__ Y,
uint CDHW, uint NDHW, uint DHW, int relu)
{
__shared__ float Share[32];
uint tid = threadIdx.x;
if (blockDim.x > 32)
{
if (tid < 32)
Share[tid] = 0.0f;
__syncthreads();
}
uint c = blockIdx.x;
uint offsetC = c * DHW;
float db = 0.0f;
#pragma unroll 1
for (int ndhw = tid; ndhw < NDHW; ndhw += blockDim.x)
{
uint n = ndhw / DHW;
uint dhw = ndhw % DHW;
uint offset = offsetC + n * CDHW + dhw;
float dy = load(add_ptr_u(DY, offset));
if (relu)
{
float y = load(add_ptr_u(Y, offset));
dy = ew_relu_grad(dy, y);
store(add_ptr_u(DX, offset), dy);
}
db += dy;
}
// reduce within warp
for (int i = 16; i > 0; i >>= 1)
db += shfl_xor(db, i);
if (blockDim.x > 32)
{
// first thread of each warp store to shared
if ((tid & 31) == 0)
Share[tid/32] = db;
__syncthreads();
if (tid < 32)
{
// first warp loads all prior reductions
db = Share[tid];
// reduce within this last warp
#pragma unroll 1
for (int i = blockDim.x/64; i > 0; i >>= 1)
db += shfl_xor(db, i);
}
}
// single thread outputs final reductions
if (tid == 0)
DB[c] = db;
}
template <typename T>
bool CWiseLinear_Forward(CUstream stream,
T* y,
const T* x,
const float* a,
const float* b,
uint N, uint C, uint DHW, bool relu, bool swap)
{
// target 4 loops per block
uint threads =
DHW <= 4*32 ? 32 :
DHW <= 8*32 ? 64 :
DHW <= 16*32 ? 128 :
DHW <= 32*32 ? 256 :
DHW <= 64*32 ? 512 : 1024;
cwise_linear_axpb_forward<T><<<dim3(C, N, 1),threads,0,stream>>>(y, x, a, b, C*DHW, DHW, a!=0, b!=0, relu, swap);
return true; // TODO
}
template <typename T>
bool CWiseLinear_Backward(CUstream stream,
T* dx,
float* da,
float* db,
const T* dy,
const T* xy,
const float* a,
const float* b,
uint N, uint C, uint DHW, bool relu, bool swap)
{
dim3 grid(C, 1, 1);
uint NDHW = N*DHW;
uint CDHW = C*DHW;
// target 4 loops per block
uint threads =
NDHW <= 4*32 ? 32 :
NDHW <= 8*32 ? 64 :
NDHW <= 16*32 ? 128 :
NDHW <= 32*32 ? 256 :
NDHW <= 64*32 ? 512 : 1024;
if (da != NULL)
cwise_linear_axpb_backward<T><<<grid,threads,0,stream>>>(dx, da, db, dy, xy, a, b, CDHW, NDHW, DHW, db!=0, relu, swap);
else
cwise_linear_xpb_backward<T><<<grid,threads,0,stream>>>(dx, db, dy, xy, CDHW, NDHW, DHW, relu);
return true; // TODO
}
template bool CWiseLinear_Forward<float>(CUstream stream, float* y, const float* x, const float* a, const float* b, uint N, uint C, uint DHW, bool relu, bool swap);
template bool CWiseLinear_Forward<ehalf>(CUstream stream, ehalf* y, const ehalf* x, const float* a, const float* b, uint N, uint C, uint DHW, bool relu, bool swap);
template bool CWiseLinear_Forward<bhalf>(CUstream stream, bhalf* y, const bhalf* x, const float* a, const float* b, uint N, uint C, uint DHW, bool relu, bool swap);
template bool CWiseLinear_Backward<float>(CUstream stream, float* dx, float* da, float* db, const float* dy, const float* xy, const float* a, const float* b, uint N, uint C, uint DHW, bool relu, bool swap);
template bool CWiseLinear_Backward<ehalf>(CUstream stream, ehalf* dx, float* da, float* db, const ehalf* dy, const ehalf* xy, const float* a, const float* b, uint N, uint C, uint DHW, bool relu, bool swap);
template bool CWiseLinear_Backward<bhalf>(CUstream stream, bhalf* dx, float* da, float* db, const bhalf* dy, const bhalf* xy, const float* a, const float* b, uint N, uint C, uint DHW, bool relu, bool swap);
#endif