#include "../dispatch/dispatcher.h" #include #include #include #define TOLERANCE 1e-5f typedef struct { size_t total; size_t passed; size_t failed; } test_result_t; test_result_t result = {0, 0, 0}; void assert_equal(float expected, float actual, const char* test_name) { result.total++; float error = expected - actual; if (error < 0) error = -error; if (error < TOLERANCE) { result.passed++; printf(" PASS: %s\n", test_name); } else { result.failed++; printf(" FAIL: %s (expected %.6f, got %.6f, error %.2e)\n", test_name, expected, actual, error); } } void test_scalar_1x1(void) { printf("Test: 1x1 matrices\n"); matrix_t* A = matrix_allocate(1, 1, 64); matrix_t* B = matrix_allocate(1, 1, 64); matrix_t* C = matrix_allocate(1, 1, 64); matrix_set(A, 0, 0, 2.0f); matrix_set(B, 0, 0, 3.0f); matrix_zero(C); gemm_f32(A, B, C, 1.0f, 0.0f, KERNEL_SCALAR); assert_equal(6.0f, matrix_get(C, 0, 0), "1x1 scalar multiply"); matrix_free(A); matrix_free(B); matrix_free(C); } void test_scalar_2x2(void) { printf("Test: 2x2 matrices\n"); matrix_t* A = matrix_allocate(2, 2, 64); matrix_t* B = matrix_allocate(2, 2, 64); matrix_t* C = matrix_allocate(2, 2, 64); matrix_set(A, 0, 0, 1.0f); matrix_set(A, 0, 1, 2.0f); matrix_set(A, 1, 0, 3.0f); matrix_set(A, 1, 1, 4.0f); matrix_set(B, 0, 0, 5.0f); matrix_set(B, 0, 1, 6.0f); matrix_set(B, 1, 0, 7.0f); matrix_set(B, 1, 1, 8.0f); matrix_zero(C); gemm_f32(A, B, C, 1.0f, 0.0f, KERNEL_SCALAR); assert_equal(19.0f, matrix_get(C, 0, 0), "2x2 C[0,0]"); assert_equal(22.0f, matrix_get(C, 0, 1), "2x2 C[0,1]"); assert_equal(43.0f, matrix_get(C, 1, 0), "2x2 C[1,0]"); assert_equal(50.0f, matrix_get(C, 1, 1), "2x2 C[1,1]"); matrix_free(A); matrix_free(B); matrix_free(C); } void test_3x3(void) { printf("Test: 3x3 matrices\n"); matrix_t* A = matrix_allocate(3, 3, 64); matrix_t* B = matrix_allocate(3, 3, 64); matrix_t* C = matrix_allocate(3, 3, 64); for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) { matrix_set(A, i, j, (float)(i * 3 + j + 1)); matrix_set(B, i, j, (float)(i * 3 + j + 1)); } } matrix_zero(C); gemm_f32(A, B, C, 1.0f, 0.0f, KERNEL_SCALAR); assert_equal(30.0f, matrix_get(C, 0, 0), "3x3 C[0,0]"); assert_equal(36.0f, matrix_get(C, 0, 1), "3x3 C[0,1]"); assert_equal(42.0f, matrix_get(C, 0, 2), "3x3 C[0,2]"); matrix_free(A); matrix_free(B); matrix_free(C); } void test_rectangular_97x103x89(void) { printf("Test: Rectangular 97x103x89 (odd dimensions)\n"); matrix_t* A = matrix_allocate(97, 89, 64); matrix_t* B = matrix_allocate(89, 103, 64); matrix_t* C_scalar = matrix_allocate(97, 103, 64); matrix_t* C_blocked = matrix_allocate(97, 103, 64); matrix_t* C_avx2 = matrix_allocate(97, 103, 64); for (size_t i = 0; i < 97; i++) { for (size_t k = 0; k < 89; k++) { matrix_set(A, i, k, (float)(rand() % 100) / 100.0f); } } for (size_t k = 0; k < 89; k++) { for (size_t j = 0; j < 103; j++) { matrix_set(B, k, j, (float)(rand() % 100) / 100.0f); } } matrix_zero(C_scalar); matrix_zero(C_blocked); matrix_zero(C_avx2); gemm_f32(A, B, C_scalar, 1.0f, 0.0f, KERNEL_SCALAR); gemm_f32(A, B, C_blocked, 1.0f, 0.0f, KERNEL_BLOCKED); gemm_f32(A, B, C_avx2, 1.0f, 0.0f, KERNEL_AVX2); float max_error_blocked = 0.0f; float max_error_avx2 = 0.0f; for (size_t i = 0; i < 97; i++) { for (size_t j = 0; j < 103; j++) { float diff = matrix_get(C_scalar, i, j) - matrix_get(C_blocked, i, j); if (diff < 0) diff = -diff; if (diff > max_error_blocked) max_error_blocked = diff; diff = matrix_get(C_scalar, i, j) - matrix_get(C_avx2, i, j); if (diff < 0) diff = -diff; if (diff > max_error_avx2) max_error_avx2 = diff; } } printf(" Blocked vs Scalar max error: %.2e\n", max_error_blocked); printf(" AVX2 vs Scalar max error: %.2e\n", max_error_avx2); if (max_error_blocked < 1e-4f) { result.passed++; printf(" PASS: Blocked GEMM 97x103x89\n"); } else { result.failed++; printf(" FAIL: Blocked GEMM error too large\n"); } result.total++; if (max_error_avx2 < 1e-4f) { result.passed++; printf(" PASS: AVX2 GEMM 97x103x89\n"); } else { result.failed++; printf(" FAIL: AVX2 GEMM error too large\n"); } result.total++; matrix_free(A); matrix_free(B); matrix_free(C_scalar); matrix_free(C_blocked); matrix_free(C_avx2); } void test_beta_scaling(void) { printf("Test: Beta scaling (C = alpha*A*B + beta*C)\n"); matrix_t* A = matrix_allocate(2, 2, 64); matrix_t* B = matrix_allocate(2, 2, 64); matrix_t* C = matrix_allocate(2, 2, 64); matrix_set(A, 0, 0, 1.0f); matrix_set(A, 0, 1, 1.0f); matrix_set(A, 1, 0, 1.0f); matrix_set(A, 1, 1, 1.0f); matrix_set(B, 0, 0, 2.0f); matrix_set(B, 0, 1, 2.0f); matrix_set(B, 1, 0, 2.0f); matrix_set(B, 1, 1, 2.0f); matrix_set(C, 0, 0, 1.0f); matrix_set(C, 0, 1, 1.0f); matrix_set(C, 1, 0, 1.0f); matrix_set(C, 1, 1, 1.0f); gemm_f32(A, B, C, 2.0f, 0.5f, KERNEL_SCALAR); assert_equal(8.5f, matrix_get(C, 0, 0), "Beta scaled C[0,0]"); matrix_free(A); matrix_free(B); matrix_free(C); } void test_large_32x32(void) { printf("Test: 32x32 matrices\n"); matrix_t* A = matrix_allocate(32, 32, 64); matrix_t* B = matrix_allocate(32, 32, 64); matrix_t* C_ref = matrix_allocate(32, 32, 64); matrix_t* C_opt = matrix_allocate(32, 32, 64); for (size_t i = 0; i < 32 * 32; i++) { A->data[i] = (float)(rand() % 100) / 100.0f; B->data[i] = (float)(rand() % 100) / 100.0f; } matrix_zero(C_ref); matrix_zero(C_opt); gemm_f32(A, B, C_ref, 1.0f, 0.0f, KERNEL_SCALAR); gemm_f32(A, B, C_opt, 1.0f, 0.0f, KERNEL_BLOCKED); float max_error = 0.0f; for (size_t i = 0; i < 32 * 32; i++) { float diff = C_ref->data[i] - C_opt->data[i]; if (diff < 0) diff = -diff; if (diff > max_error) max_error = diff; } printf(" 32x32 max error: %.2e\n", max_error); if (max_error < 1e-4f) { result.passed++; printf(" PASS: 32x32 matrices\n"); } else { result.failed++; printf(" FAIL: 32x32 error too large\n"); } result.total++; matrix_free(A); matrix_free(B); matrix_free(C_ref); matrix_free(C_opt); } int main(void) { printf("=== BSH GEMM Test Suite ===\n\n"); test_scalar_1x1(); test_scalar_2x2(); test_3x3(); test_beta_scaling(); test_large_32x32(); test_rectangular_97x103x89(); printf("\n=== Results ===\n"); printf("Total: %zu\n", result.total); printf("Passed: %zu\n", result.passed); printf("Failed: %zu\n\n", result.failed); if (result.failed == 0) { printf("All tests passed!\n"); return 0; } else { printf("Some tests failed.\n"); return 1; } }