Day 74

GPU Kernels Learning Journey

Description

### Files: `fused_linear_softmax.cu` Today, I implemented a fused Linear transformation and Softmax cross-entropy loss computation in pure CUDA C++. The kernel performs matrix multiplication followed by Softmax activation and loss computation in a single pass to optimize memory efficiency and minimize redundant data transfers.

Code Files

fused_linear_softmax.cu

#include <cuda_runtime.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define BLOCK_SIZE 256

__global__ void LinearKernel(float* input, float* weights, float* bias, float* output, int in_features, int out_features) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < out_features) {
        float sum = bias[idx];
        for (int i = 0; i < in_features; i++) {
            sum += input[i] * weights[i * out_features + idx];
        }
        output[idx] = sum;
    }
}

__global__ void softmaxCrossEntropyKernel(float* logits, int* labels, float* loss, int num_classes) {
    extern __shared__ float exp_sums[];
    int idx = threadIdx.x;

    float max_val = -INFINITY;
    for (int i = 0; i < num_classes; i++) {
        max_val = fmaxf(max_val, logits[i]);
    }
    float sum = 0.0;
    for (int i = 0; i < num_classes; i++) {
        exp_sums[i] = expf(logits[i] - max_val);
        sum += exp_sums[i];
    }
    float log_prob = logf(exp_sums[labels[0]] / sum);
    *loss = -log_prob;
}

void runFusedOperations(float* input, float* weights, float* bias, int* label, int in_features, int out_features) {
    float *d_input, *d_weights, *d_bias, *d_output, *d_loss;
    int *d_label;

    cudaMalloc((void**)&d_input, in_features * sizeof(float));
    cudaMalloc((void**)&d_weights, in_features * out_features * sizeof(float));
    cudaMalloc((void**)&d_bias, out_features * sizeof(float));
    cudaMalloc((void**)&d_output, out_features * sizeof(float));
    cudaMalloc((void**)&d_label, sizeof(int));
    cudaMalloc((void**)&d_loss, sizeof(float));

    cudaMemcpy(d_input, input, in_features * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_weights, weights, in_features * out_features * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_bias, bias, out_features * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_label, label, sizeof(int), cudaMemcpyHostToDevice);

    LinearKernel<<<(out_features + BLOCK_SIZE - 1) / BLOCK_SIZE, BLOCK_SIZE>>>(d_input, d_weights, d_bias, d_output, in_features, out_features);
    softmaxCrossEntropyKernel<<<1, out_features, out_features * sizeof(float)>>>(d_output, d_label, d_loss, out_features);

    float loss;
    cudaMemcpy(&loss, d_loss, sizeof(float), cudaMemcpyDeviceToHost);

    printf("Cross-entropy loss: %f\n", loss);

    cudaFree(d_input);
    cudaFree(d_weights);
    cudaFree(d_bias);
    cudaFree(d_output);
    cudaFree(d_label);
    cudaFree(d_loss);
}

int main() {
    int in_features = 3;
    int out_features = 2;
    float input[3] = {1.0, 2.0, 3.0};
    float weights[6] = {0.2, 0.8, -0.5, 1.0, -1.2, 0.3};
    float bias[2] = {0.1, -0.2};
    int label = 1;

    runFusedOperations(input, weights, bias, &label, in_features, out_features);
    return 0;
}