Day 72

GPU Kernels Learning Journey

Description

### Files: `dyt.cu` I implemented the DyT (Dynamic Tanh) operation in a standalone CUDA C++ file (`dyt.cu`) without using any external frameworks. The implementation includes both the forward pass and the backward pass, which computes the gradients using atomic operations to safely accumulate values across threads. Memory allocation, data transfers, and kernel launches are manually managed, and a `main` function is provided to test the kernels by processing sample data and printing the results.

Code Files

dyt.cu

#include <cuda_runtime.h>
#include <iostream>
#include <cmath>

#define BLOCK_SIZE 256

__device__ float tanh_activation(float x) {
    return tanhf(x);
}

__global__ void dyt_fwd_kernel(
    const float* x, float* y,
    const float alpha,
    const float* gamma, const float* beta,
    int n_cols
) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < n_cols) {
        float val = alpha * x[idx];
        y[idx] = gamma[idx] * tanh_activation(val) + beta[idx];
    }
}

__global__ void dyt_bwd_kernel(
    const float* x, const float* dy,
    float* dx, float* dalpha, float* dgamma, float* dbeta,
    float alpha, const float* gamma,
    int n_cols
) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < n_cols) {
        float tanh_ax = tanh_activation(alpha * x[idx]);
        float sech2_ax = 1 - tanh_ax * tanh_ax;
        
        dx[idx] = dy[idx] * gamma[idx] * sech2_ax * alpha;
        atomicAdd(dalpha, dy[idx] * gamma[idx] * sech2_ax * x[idx]);
        atomicAdd(&dgamma[idx], dy[idx] * tanh_ax);
        atomicAdd(&dbeta[idx], dy[idx]);
    }
}

void liger_dyt_fwd(float* x, float* y, float alpha, float* gamma, float* beta, int n_cols) {
    float *d_x, *d_y, *d_gamma, *d_beta;
    cudaMalloc(&d_x, n_cols * sizeof(float));
    cudaMalloc(&d_y, n_cols * sizeof(float));
    cudaMalloc(&d_gamma, n_cols * sizeof(float));
    cudaMalloc(&d_beta, n_cols * sizeof(float));

    cudaMemcpy(d_x, x, n_cols * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_gamma, gamma, n_cols * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_beta, beta, n_cols * sizeof(float), cudaMemcpyHostToDevice);
    
    dyt_fwd_kernel<<<(n_cols + BLOCK_SIZE - 1) / BLOCK_SIZE, BLOCK_SIZE>>>(d_x, d_y, alpha, d_gamma, d_beta, n_cols);
    cudaMemcpy(y, d_y, n_cols * sizeof(float), cudaMemcpyDeviceToHost);
    
    cudaFree(d_x);
    cudaFree(d_y);
    cudaFree(d_gamma);
    cudaFree(d_beta);
}

void liger_dyt_bwd(float* x, float* dy, float* dx, float* dalpha, float* dgamma, float* dbeta, float alpha, float* gamma, int n_cols) {
    float *d_x, *d_dy, *d_dx, *d_dalpha, *d_dgamma, *d_dbeta, h_dalpha = 0;
    cudaMalloc(&d_x, n_cols * sizeof(float));
    cudaMalloc(&d_dy, n_cols * sizeof(float));
    cudaMalloc(&d_dx, n_cols * sizeof(float));
    cudaMalloc(&d_dalpha, sizeof(float));
    cudaMalloc(&d_dgamma, n_cols * sizeof(float));
    cudaMalloc(&d_dbeta, n_cols * sizeof(float));

    cudaMemcpy(d_x, x, n_cols * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_dy, dy, n_cols * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_dalpha, &h_dalpha, sizeof(float), cudaMemcpyHostToDevice);
    
    dyt_bwd_kernel<<<(n_cols + BLOCK_SIZE - 1) / BLOCK_SIZE, BLOCK_SIZE>>>(d_x, d_dy, d_dx, d_dalpha, d_dgamma, d_dbeta, alpha, gamma, n_cols);
    
    cudaMemcpy(dx, d_dx, n_cols * sizeof(float), cudaMemcpyDeviceToHost);
    cudaMemcpy(dalpha, d_dalpha, sizeof(float), cudaMemcpyDeviceToHost);
    cudaMemcpy(dgamma, d_dgamma, n_cols * sizeof(float), cudaMemcpyDeviceToHost);
    cudaMemcpy(dbeta, d_dbeta, n_cols * sizeof(float), cudaMemcpyDeviceToHost);
    
    cudaFree(d_x);
    cudaFree(d_dy);
    cudaFree(d_dx);
    cudaFree(d_dalpha);
    cudaFree(d_dgamma);
    cudaFree(d_dbeta);
}

int main() {
    const int n_cols = 8;
    float x[n_cols] = {0.5, -0.3, 0.7, 1.2, -1.5, 0.9, -0.4, 0.2};
    float gamma[n_cols] = {1, 1, 1, 1, 1, 1, 1, 1};
    float beta[n_cols] = {0, 0, 0, 0, 0, 0, 0, 0};
    float alpha = 0.8;
    float y[n_cols], dx[n_cols], dalpha, dgamma[n_cols], dbeta[n_cols];

    liger_dyt_fwd(x, y, alpha, gamma, beta, n_cols);
    
    std::cout << "Forward Output: ";
    for (int i = 0; i < n_cols; ++i) {
        std::cout << y[i] << " ";
    }
    std::cout << std::endl;

    float dy[n_cols] = {1, 1, 1, 1, 1, 1, 1, 1};
    liger_dyt_bwd(x, dy, dx, &dalpha, dgamma, dbeta, alpha, gamma, n_cols);
    
    std::cout << "Backward dx: ";
    for (int i = 0; i < n_cols; ++i) {
        std::cout << dx[i] << " ";
    }
    std::cout << "\nBackward dalpha: " << dalpha << std::endl;
    return 0;
}