Files
gpt-2-inference-engine/output_verification_rand.cu
T
2026-06-07 13:43:20 -05:00

499 lines
18 KiB
Plaintext

//-----------------------------------------------------------------------------------------------
// Copyright (c) 2024 Andrej Karpathy
// Licensed under the MIT License. See the LICENSE file for details.
//
// Modifications Copyright (c) 2025 Hanwen Liu, Hrishi Shah, Kelin Zeng, Charles Pei, and Vijay Daita, ALL RIGHTS RESERVED.
//-----------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <cuda_runtime.h>
#include <math.h>
#include <float.h>
#include <cublas_v2.h>
#include <cuda_runtime.h>
#include <iostream>
// Include CPU kernels
#include "cpu_kernels/attention.cuh"
#include "cpu_kernels/encoder.cuh"
#include "cpu_kernels/gelu.cuh"
#include "cpu_kernels/layernorm.cuh"
#include "cpu_kernels/matmul.cuh"
#include "cpu_kernels/residual.cuh"
// Include GPU kernels
#include "kernels/attention.cuh"
#include "kernels/encoder.cuh"
#include "kernels/gelu.cuh"
#include "kernels/layernorm.cuh"
#include "kernels/matmul.cuh"
#include "kernels/residual.cuh"
#include "kernels/softmax.cuh"
// Include CUDA utilities
#include "utils/cuda_utils.cuh"
int main() {
// set up the device
int deviceIdx = 0;
cudaCheck(cudaSetDevice(deviceIdx));
cudaDeviceProp deviceProp;
cudaGetDeviceProperties(&deviceProp, deviceIdx);
// setup cuBLAS and cuBLASLt
cublasCheck(cublasCreate(&cublas_handle));
// TF32 precision is equivalent to torch.set_float32_matmul_precision('high')
int enable_tf32 = 0;
cublas_compute_type = enable_tf32 ? CUBLAS_COMPUTE_32F_FAST_TF32 : CUBLAS_COMPUTE_32F;
cublasMath_t cublas_math_mode = enable_tf32 ? CUBLAS_TF32_TENSOR_OP_MATH : CUBLAS_DEFAULT_MATH;
cublasCheck(cublasSetMathMode(cublas_handle, cublas_math_mode));
const int B = 4; // Batch size
const int T = 128; // Sequence length
const int C = 128; // Input channels
const int OC = 4 * C; // Output channels
const int NH = 12; // Number of heads
const float error_threshold = 0; // TODO: Set the error threshold for GPU vs CPU output comparisons
// This is in unit of percentage difference in output values
// Change this to a small value (like 0.01) for your own testing
const float ep = 0.0001; // Epsilon value for percentage calculation to avoid getting infinity
// used in multi-head self-attention
const int HS = C / NH; // head size
// Generate random input data
srand(time(NULL));
/*------------------------ encoder_forward test ------------------------*/
// Allocate memory for input data
int* enc_inp_cpu = (int*)malloc(B * T * sizeof(int));
float* wte_enc = (float*)malloc(C * C * sizeof(float));
float* wpe_enc = (float*)malloc(T * C * sizeof(float));
for (int i = 0; i < B * T; ++i) {
enc_inp_cpu[i] = (int)rand() / RAND_MAX;
}
for (int i = 0; i < C * C; ++i) {
wte_enc[i] = (float)rand() / RAND_MAX;
}
for (int i = 0; i < T * C; ++i) {
wpe_enc[i] = (float)rand() / RAND_MAX;
}
// copy data to GPU
int* enc_inp_gpu_d;
float* wte_enc_d;
float* wpe_enc_d;
cudaMalloc(&enc_inp_gpu_d, B * T * sizeof(int));
cudaMalloc(&wte_enc_d, C * C * sizeof(float));
cudaMalloc(&wpe_enc_d, T * C * sizeof(float));
cudaMemcpy(enc_inp_gpu_d, enc_inp_cpu, B * T * sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(wte_enc_d, wte_enc, C * C * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(wpe_enc_d, wpe_enc, T * C * sizeof(float), cudaMemcpyHostToDevice);
// Allocate memory for output
float* enc_out_cpu = (float*)malloc(B * T * C * sizeof(float));
float* enc_out_gpu = (float*)malloc(B * T * C * sizeof(float));
float* enc_out_gpu_d;
cudaMalloc(&enc_out_gpu_d, B * T * C * sizeof(float));
// Call CPU function for reference output
encoder_forward_cpu(enc_out_cpu, enc_inp_cpu, wte_enc, wpe_enc, B, T, C);
// TODO: Call your GPU function
// encoder_forward(enc_out_gpu_d, enc_inp_gpu_d, wte_enc_d, wpe_enc_d, B, T, C);
// TODO: Copy output from your GPU function back to CPU
// cudaMemcpy(..., cudaMemcpyDeviceToHost);
int enc_totElements = B * T * C;
int enc_error = 0;
for (int i = 0; i < enc_totElements; i++) {
if (fabs(enc_out_cpu[i] - enc_out_gpu[i]) / (enc_out_cpu[i] + ep) > error_threshold) {
enc_error++;
}
}
if (enc_error > 0) {
printf("+++++++++++++++ encoder_forward test failed with %d differing values +++++++++++++++\n", enc_error);
printf("+++++++++++++++ Check your implementation or error_threshold value +++++++++++++++\n");
} else {
printf("--------------------encoder_forward test passed!--------------------\n");
}
/*------------------------ layernorm_forward test ------------------------*/
// Allocate memory for input data
float* lay_inp_cpu = (float*)malloc(B * T * C * sizeof(float));
float* weight_ln = (float*)malloc(C * sizeof(float));
float* bias_ln = (float*)malloc(C * sizeof(float));
float* rstd_ln = (float*)malloc(B * T * sizeof(float));
float* mean_ln = (float*)malloc(B * T * sizeof(float));
// Generate random input data
for (int i = 0; i < B * T * C; ++i) {
lay_inp_cpu[i] = (float)rand() / RAND_MAX;
}
for (int i = 0; i < B * T; ++i) {
rstd_ln[i] = (float)rand() / RAND_MAX;
mean_ln[i] = (float)rand() / RAND_MAX;
}
for (int i = 0; i < C; ++i) {
weight_ln[i] = (float)rand() / RAND_MAX;
bias_ln[i] = (float)rand() / RAND_MAX;
}
// copy data to GPU
float* lay_inp_gpu_d;
float* weight_ln_d;
float* bias_ln_d;
float* rstd_ln_d;
float* mean_ln_d;
cudaMalloc(&lay_inp_gpu_d, B * T * C * sizeof(float));
cudaMalloc(&weight_ln_d, C * sizeof(float));
cudaMalloc(&bias_ln_d, C * sizeof(float));
cudaMalloc(&rstd_ln_d, B * T * sizeof(float));
cudaMalloc(&mean_ln_d, B * T * sizeof(float));
cudaMemcpy(lay_inp_gpu_d, lay_inp_cpu, B * T * C * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(weight_ln_d, weight_ln, C * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(bias_ln_d, bias_ln, C * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(rstd_ln_d, rstd_ln, B * T * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(mean_ln_d, mean_ln, B * T * sizeof(float), cudaMemcpyHostToDevice);
// Allocate memory for output
float* lay_out_cpu = (float*)malloc(B * T * C * sizeof(float));
float* lay_out_gpu = (float*)malloc(B * T * C * sizeof(float));
float* lay_out_gpu_d;
cudaMalloc(&lay_out_gpu_d, B * T * C * sizeof(float));
// Call CPU function for reference output
layernorm_forward_cpu(lay_out_cpu, mean_ln, rstd_ln, lay_inp_cpu, weight_ln, bias_ln, B, T, C);
// TODO: Call your GPU function
// layernorm_forward(lay_out_gpu_d, mean_ln_d, rstd_ln_d, lay_inp_gpu_d, weight_ln_d, bias_ln_d, B, T, C);
// TODO: Copy output from your GPU function back to CPU
// cudaMemcpy(..., cudaMemcpyDeviceToHost);
int lay_totElements = B * T * C;
int lay_error = 0;
for (int i = 0; i < lay_totElements; i++) {
if (fabs(lay_out_cpu[i] - lay_out_gpu[i])/(lay_out_cpu[i] + ep) > error_threshold) {
lay_error++;
}
}
if (lay_error > 0) {
printf("+++++++++++++++ layernorm_forward test failed with %d differing values +++++++++++++++\n", lay_error);
printf("+++++++++++++++ Check your implementation or error_threshold value +++++++++++++++\n");
} else {
printf("--------------------layernorm_forward test passed!--------------------\n");
}
/*------------------------ attention_forward test ------------------------*/
// Allocate memory for input data
int inp_size = B * T * max(3 * C, NH * T);
float* att_inp_cpu = (float*)malloc(inp_size * sizeof(float));
float* qkvr = (float*)malloc(3 * B * T * C * sizeof(float));
float* att = (float*)malloc(B * NH * NH * T * T * sizeof(float));
// create separate copy of input for GPU to avoid input memory being overwritten
float* att_inp_gpu = (float*)malloc(inp_size * sizeof(float));
// Generate random input data
for (int i = 0; i < inp_size; ++i) {
att_inp_cpu[i] = (float)rand() / RAND_MAX;
att_inp_gpu[i] = att_inp_cpu[i];
}
// copy data to GPU
float* att_inp_gpu_d;
float* qkvr_d;
float* att_d;
cudaMalloc(&att_inp_gpu_d, inp_size * sizeof(float));
cudaMalloc(&qkvr_d, 3 * B * T * C * sizeof(float));
cudaMalloc(&att_d, B * NH * NH * T * T * sizeof(float));
cudaMemcpy(att_inp_gpu_d, att_inp_gpu, inp_size * sizeof(float), cudaMemcpyHostToDevice);
// Allocate memory for output
float* att_out_cpu = (float*)malloc(B * T * C * sizeof(float));
float* att_out_gpu = (float*)malloc(B * T * C * sizeof(float));
float* att_out_gpu_d;
cudaMalloc(&att_out_gpu_d, B * T * C * sizeof(float));
// Call CPU function for reference output
attention_forward_cpu(att_out_cpu, qkvr, att, att_inp_cpu, B, T, C, NH);
// TODO: Call your GPU function
// attention_forward(att_out_gpu_d, qkvr_d, att_d, att_inp_gpu_d, B, T, C, NH);
// TODO: Copy output from your GPU function back to CPU
// cudaMemcpy(..., cudaMemcpyDeviceToHost);
int att_totElements = B * T * C;
int att_diff = 0;
// Loop through the results to count differences
for (int i = 0; i < att_totElements; ++i) {
// Check if the result differs by more than the tolerance
if (fabs(att_out_cpu[i] - att_out_gpu[i])/(att_out_cpu[i] + ep) > error_threshold) {
++att_diff;
}
}
if (att_diff > 0) {
printf("+++++++++++++++ attention_forward test failed with %d differing values +++++++++++++++\n", att_diff);
printf("+++++++++++++++ Check your implementation or error_threshold value +++++++++++++++\n");
printf("++++++++ Try testing each attention component separately for easier debugging ++++++++\n");
} else {
printf("--------------------attention_forward test passed!--------------------\n");
}
/*------------------------ residual_forward test ------------------------*/
// Allocate memory for input data
float* res_inp1_cpu = (float*)malloc(B * T * C * sizeof(float));
float* res_inp2_cpu = (float*)malloc(B * T * C * sizeof(float));
float* res_out_cpu = (float*)malloc(B * T * C * sizeof(float));
float* res_out_gpu = (float*)malloc(B * T * C * sizeof(float));
// Generate random input data
for (int i = 0; i < B * T * C; ++i) {
res_inp1_cpu[i] = (float)rand() / RAND_MAX;
res_inp2_cpu[i] = (float)rand() / RAND_MAX;
}
// copy data to GPU
float* res_inp1_gpu_d;
float* res_inp2_gpu_d;
float* res_out_gpu_d;
cudaMalloc(&res_inp1_gpu_d, B * T * C * sizeof(float));
cudaMalloc(&res_inp2_gpu_d, B * T * C * sizeof(float));
cudaMalloc(&res_out_gpu_d, B * T * C * sizeof(float));
cudaMemcpy(res_inp1_gpu_d, res_inp1_cpu, B * T * C * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(res_inp2_gpu_d, res_inp2_cpu, B * T * C * sizeof(float), cudaMemcpyHostToDevice);
// Call CPU function for reference output
residual_forward_cpu(res_out_cpu, res_inp1_cpu, res_inp2_cpu, B * T * C);
// TODO: Call your GPU function
// residual_forward(res_out_gpu_d, res_inp1_gpu_d, res_inp2_gpu_d, B * T * C);
// TODO: Copy output from your GPU function back to CPU
// cudaMemcpy(..., cudaMemcpyDeviceToHost);
int res_totElements = B * T * C;
int res_diff = 0;
// Loop through the results to count differences
for (int i = 0; i < res_totElements; ++i) {
// Check if the result differs by more than the tolerance
if (fabs(res_out_cpu[i] - res_out_gpu[i])/ (res_out_cpu[i] + ep) > error_threshold) {
++res_diff;
}
}
if (res_diff > 0) {
printf("+++++++++++++++ residual_forward test failed with %d differing values +++++++++++++++\n", res_diff);
printf("+++++++++++++++ Check your implementation or error_threshold value +++++++++++++++\n");
} else {
printf("--------------------residual_forward test passed!--------------------\n");
}
/*------------------------ gelu_forward test ------------------------*/
// Allocate memory for input data
float* gelu_inp_cpu = (float*)malloc(B * T * C * sizeof(float));
float* gelu_out_cpu = (float*)malloc(B * T * C * sizeof(float));
float* gelu_out_gpu = (float*)malloc(B * T * C * sizeof(float));
// Generate random input data
for (int i = 0; i < B * T * C; ++i) {
gelu_inp_cpu[i] = (float)rand() / RAND_MAX;
}
// copy data to GPU
float* gelu_inp_gpu_d;
float* gelu_out_gpu_d;
cudaMalloc(&gelu_inp_gpu_d, B * T * C * sizeof(float));
cudaMalloc(&gelu_out_gpu_d, B * T * C * sizeof(float));
cudaMemcpy(gelu_inp_gpu_d, gelu_inp_cpu, B * T * C * sizeof(float), cudaMemcpyHostToDevice);
// Call CPU function for reference output
gelu_forward_cpu(gelu_out_cpu, gelu_inp_cpu, B * T * C);
// TODO: Call your GPU function
// gelu_forward(gelu_out_gpu_d, gelu_inp_gpu_d, B * T * C);
// TODO: Copy output from your GPU function back to CPU
// cudaMemcpy(..., cudaMemcpyDeviceToHost);
int gelu_totElements = B * T * C;
int gelu_diff = 0;
for (int i = 0; i < gelu_totElements; ++i) {
if (fabs(gelu_out_cpu[i] - gelu_out_gpu[i])/(gelu_out_cpu[i] + ep) > error_threshold) {
++gelu_diff;
}
}
if (gelu_diff > 0) {
printf("+++++++++++++++ gelu_forward test failed with %d differing values +++++++++++++++\n", gelu_diff);
printf("+++++++++++++++ Check your implementation or error_threshold value +++++++++++++++\n");
} else {
printf("--------------------gelu_forward test passed!--------------------\n");
}
/*------------------------ matmul_forward test ------------------------*/
// Allocate memory for input data
float* mat_inp_cpu = (float*)malloc(B * T * C * sizeof(float));
float* matmul_weight = (float*)malloc(4 * C * C * sizeof(float));
float* matmul_bias = (float*)malloc(4 * C * sizeof(float));
float* mat_out_cpu = (float*)malloc(B * T * 4 * C * sizeof(float));
// Generate random input data
for (int i = 0; i < B * T * C; ++i) {
mat_inp_cpu[i] = (float)rand() / RAND_MAX;
}
for (int i = 0; i < 4 * C * C; ++i) {
matmul_weight[i] = (float)rand() / RAND_MAX;
}
for (int i = 0; i < 4 * C; ++i) {
matmul_bias[i] = (float)rand() / RAND_MAX;
}
// copy data to GPU
float* mat_inp_gpu_d;
float* matmul_weight_d;
float* matmul_bias_d;
float* mat_out_gpu_d;
float* mat_out_gpu = (float*)malloc(B * T * 4 * C * sizeof(float));
cudaMalloc(&mat_inp_gpu_d, B * T * C * sizeof(float));
cudaMalloc(&matmul_weight_d, 4 * C * C * sizeof(float));
cudaMalloc(&matmul_bias_d, 4 * C * sizeof(float));
cudaMalloc(&mat_out_gpu_d, B * T * 4 * C * sizeof(float));
cudaMemcpy(mat_inp_gpu_d, mat_inp_cpu, B * T * C * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(matmul_weight_d, matmul_weight, 4 * C * C * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(matmul_bias_d, matmul_bias, 4 * C * sizeof(float), cudaMemcpyHostToDevice);
// Call CPU function for reference output
matmul_forward_cpu(mat_out_cpu, mat_inp_cpu, matmul_weight, matmul_bias, B, T, C, OC);
// TODO: Call your GPU function
// matmul_forward(mat_out_gpu_d, mat_inp_gpu_d, matmul_weight_d, matmul_bias_d, B, T, C, OC);
// TODO: Copy output from your GPU function back to CPU
// cudaMemcpy(..., cudaMemcpyDeviceToHost);
int mat_totElements = B * T * 4 * C;
int mat_diff = 0;
for (int i = 0; i < mat_totElements; ++i) {
if (fabs(mat_out_cpu[i] - mat_out_gpu[i])/(mat_out_cpu[i] + ep) > error_threshold) {
++mat_diff;
}
}
if (mat_diff > 0) {
printf("+++++++++++++++ matmul_forward test failed with %d differing values +++++++++++++++\n", mat_diff);
printf("+++++++++++++++ Check your implementation or error_threshold value +++++++++++++++\n");
} else {
printf("--------------------matmul_forward test passed!--------------------\n");
}
printf("********************* All Random Tests Finished! *********************\n");
// Free allocated memory
free(enc_inp_cpu);
free(enc_out_cpu);
free(wte_enc);
free(wpe_enc);
free(enc_out_gpu);
cudaFree(enc_inp_gpu_d);
cudaFree(wte_enc_d);
cudaFree(wpe_enc_d);
cudaFree(enc_out_gpu_d);
free(lay_inp_cpu);
free(lay_out_cpu);
free(weight_ln);
free(bias_ln);
free(rstd_ln);
free(mean_ln);
free(lay_out_gpu);
cudaFree(lay_inp_gpu_d);
cudaFree(weight_ln_d);
cudaFree(bias_ln_d);
cudaFree(rstd_ln_d);
cudaFree(mean_ln_d);
cudaFree(lay_out_gpu_d);
free(perm_inp_cpu);
free(perm_q_cpu);
free(perm_k_cpu);
free(perm_v_cpu);
free(perm_q_gpu);
free(perm_k_gpu);
free(perm_v_gpu);
cudaFree(perm_inp_gpu_d);
cudaFree(perm_q_d);
cudaFree(perm_k_d);
cudaFree(perm_v_d);
free(unperm_inp_cpu);
free(unperm_out_cpu);
free(unperm_out_gpu);
cudaFree(unperm_inp_gpu_d);
cudaFree(unperm_out_gpu_d);
free(softmax_inp_cpu);
free(softmax_inp_gpu);
free(softmax_out_cpu);
free(softmax_out_gpu);
cudaFree(softmax_inp_gpu_d);
cudaFree(softmax_out_gpu_d);
free(att_inp_cpu);
free(att_out_cpu);
free(att_inp_gpu);
free(att_out_gpu);
free(qkvr);
free(att);
cudaFree(att_inp_gpu_d);
cudaFree(qkvr_d);
cudaFree(att_d);
cudaFree(att_out_gpu_d);
free(res_inp1_cpu);
free(res_inp2_cpu);
free(res_out_cpu);
free(res_out_gpu);
cudaFree(res_inp1_gpu_d);
cudaFree(res_inp2_gpu_d);
cudaFree(res_out_gpu_d);
free(gelu_inp_cpu);
free(gelu_out_cpu);
free(gelu_out_gpu);
cudaFree(gelu_inp_gpu_d);
cudaFree(gelu_out_gpu_d);
free(mat_inp_cpu);
free(mat_out_gpu);
free(matmul_weight);
free(matmul_bias);
cudaFree(mat_inp_gpu_d);
cudaFree(matmul_weight_d);
cudaFree(matmul_bias_d);
cudaFree(mat_out_gpu_d);
return 0;
}