| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <cuda_runtime.h> |
| #include <stdint.h> |
|
|
| #define GROUP_SIZE 64 |
| #define WARP_SIZE 32 |
| #define TRIT_POS 1 |
| #define TRIT_NEG 2 |
|
|
| |
| static void set_l2_persist(void* ptr, size_t bytes); |
| static void clear_l2_persist(); |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #define V28_RPB 16 |
| #define V28_WPG 8 |
| #define V28_BS (V28_RPB * WARP_SIZE) |
|
|
| __global__ void k_v28( |
| const uint32_t* __restrict__ pt, |
| const float* __restrict__ ws, |
| const uint32_t* __restrict__ xt_e, |
| const uint32_t* __restrict__ xt_o, |
| const float* __restrict__ xs, |
| float* __restrict__ y, |
| int cols, int rows, int num_groups |
| ) { |
| int wid = threadIdx.x / WARP_SIZE; |
| int lane = threadIdx.x % WARP_SIZE; |
| int row = blockIdx.x * V28_RPB + wid; |
| if (row >= rows) return; |
|
|
| const uint32_t* row_w = &pt[row * num_groups * V28_WPG]; |
| const float* row_ws = &ws[row * num_groups]; |
|
|
| float acc = 0.0f; |
| int total_words = num_groups * V28_WPG; |
|
|
| for (int base = 0; base < total_words; base += WARP_SIZE) { |
| int w = base + lane; |
| if (w < total_words) { |
| |
| uint32_t word = __ldg(&row_w[w]); |
| int g = w >> 3; |
| int word_in_group = w & 7; |
|
|
| |
| |
| uint32_t evens = word & 0x0F0F0F0F; |
| evens = (evens ^ 0x08080808) - 0x08080808; |
|
|
| |
| uint32_t odds = (word >> 4) & 0x0F0F0F0F; |
| odds = (odds ^ 0x08080808) - 0x08080808; |
| |
|
|
| |
| |
| |
| int x_idx = g * 8 + word_in_group; |
| uint32_t xe = __ldg(&xt_e[x_idx]); |
| uint32_t xo = __ldg(&xt_o[x_idx]); |
|
|
| int dp_e = __dp4a((int)evens, (int)xe, 0); |
| int dp_o = __dp4a((int)odds, (int)xo, 0); |
|
|
| float combined_scale = __ldg(&row_ws[g]) * __ldg(&xs[g]); |
| acc += (float)(dp_e + dp_o) * combined_scale; |
| } |
| } |
|
|
| #pragma unroll |
| for (int o = 16; o > 0; o >>= 1) |
| acc += __shfl_down_sync(0xFFFFFFFF, acc, o); |
|
|
| if (lane == 0) y[row] = acc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #define V29_RPB 16 |
| #define V29_WPG 8 |
| #define V29_BS (V29_RPB * WARP_SIZE) |
|
|
| __global__ void k_v29( |
| const uint32_t* __restrict__ pt, |
| const float* __restrict__ ws, |
| const uint32_t* __restrict__ xt_e, |
| const uint32_t* __restrict__ xt_o, |
| const int* __restrict__ x_bias, |
| const float* __restrict__ xs, |
| float* __restrict__ y, |
| int cols, int rows, int num_groups |
| ) { |
| int wid = threadIdx.x / WARP_SIZE; |
| int lane = threadIdx.x % WARP_SIZE; |
| int row = blockIdx.x * V29_RPB + wid; |
| if (row >= rows) return; |
|
|
| const uint32_t* row_w = &pt[row * num_groups * V29_WPG]; |
| const float* row_ws = &ws[row * num_groups]; |
|
|
| float acc = 0.0f; |
| int total_words = num_groups * V29_WPG; |
|
|
| for (int base = 0; base < total_words; base += WARP_SIZE) { |
| int w = base + lane; |
| if (w < total_words) { |
| uint32_t word = __ldg(&row_w[w]); |
| int g = w >> 3; |
| int wig = w & 7; |
|
|
| |
| uint32_t evens = word & 0x0F0F0F0F; |
| uint32_t odds = (word >> 4) & 0x0F0F0F0F; |
| |
|
|
| int x_idx = g * 8 + wig; |
| uint32_t xe = __ldg(&xt_e[x_idx]); |
| uint32_t xo = __ldg(&xt_o[x_idx]); |
|
|
| |
| int dp = __dp4a((int)evens, (int)xe, 0) |
| + __dp4a((int)odds, (int)xo, 0); |
|
|
| |
| int bias = __ldg(&x_bias[x_idx]); |
| dp -= bias; |
|
|
| float combined_scale = __ldg(&row_ws[g]) * __ldg(&xs[g]); |
| acc += (float)dp * combined_scale; |
| } |
| } |
|
|
| #pragma unroll |
| for (int o = 16; o > 0; o >>= 1) |
| acc += __shfl_down_sync(0xFFFFFFFF, acc, o); |
|
|
| if (lane == 0) y[row] = acc; |
| } |
|
|
| |
|
|
| |
| __device__ __forceinline__ uint32_t extract_int4x4_to_int8x4(uint32_t word, int start) { |
| uint32_t result = 0; |
| #pragma unroll |
| for (int i = 0; i < 4; i++) { |
| int shift = (start + i) * 4; |
| int nibble = (word >> shift) & 0xF; |
| int val = (nibble & 0x8) ? (nibble | 0xFFFFFFF0) : nibble; |
| result |= ((uint32_t)(val & 0xFF)) << (i * 8); |
| } |
| return result; |
| } |
|
|
| #define V27_RPB 16 |
| #define V27_WPG 8 |
| #define V27_BS (V27_RPB * WARP_SIZE) |
|
|
| __global__ void k_v27( |
| const uint32_t* __restrict__ pt, |
| const float* __restrict__ ws, |
| const uint32_t* __restrict__ xt, |
| const float* __restrict__ xs, |
| float* __restrict__ y, |
| int cols, int rows, int num_groups |
| ) { |
| int wid = threadIdx.x / WARP_SIZE; |
| int lane = threadIdx.x % WARP_SIZE; |
| int row = blockIdx.x * V28_RPB + wid; |
| if (row >= rows) return; |
|
|
| const uint32_t* row_w = &pt[row * num_groups * V27_WPG]; |
| const float* row_ws = &ws[row * num_groups]; |
|
|
| float acc = 0.0f; |
| int total_words = num_groups * V27_WPG; |
|
|
| for (int base = 0; base < total_words; base += WARP_SIZE) { |
| int w = base + lane; |
| if (w < total_words) { |
| uint32_t word = __ldg(&row_w[w]); |
| int g = w >> 3; |
| int word_in_group = w & 7; |
|
|
| uint32_t lo = extract_int4x4_to_int8x4(word, 0); |
| uint32_t hi = extract_int4x4_to_int8x4(word, 4); |
|
|
| int x_base = g * 16 + word_in_group * 2; |
| uint32_t x_lo = __ldg(&xt[x_base]); |
| uint32_t x_hi = __ldg(&xt[x_base + 1]); |
|
|
| int dp_lo = __dp4a((int)lo, (int)x_lo, 0); |
| int dp_hi = __dp4a((int)hi, (int)x_hi, 0); |
|
|
| float combined_scale = __ldg(&row_ws[g]) * __ldg(&xs[g]); |
| acc += (float)(dp_lo + dp_hi) * combined_scale; |
| } |
| } |
|
|
| #pragma unroll |
| for (int o = 16; o > 0; o >>= 1) |
| acc += __shfl_down_sync(0xFFFFFFFF, acc, o); |
|
|
| if (lane == 0) y[row] = acc; |
| } |
|
|
| |
| |
| |
|
|
| #define V9R 4 |
| #define V9W 2 |
| #define V9BS (V9R * V9W * WARP_SIZE) |
|
|
| __device__ __forceinline__ float mac_wide_d3( |
| const uint32_t* __restrict__ p, const float* __restrict__ x, int tid |
| ) { |
| float acc = 0.0f; |
| #pragma unroll |
| for (int i = 0; i < 4; i++) { |
| int idx = tid * 4 + i; |
| int w = idx / 5, pos = idx % 5; |
| uint32_t bits = (__ldg(&p[w]) >> (pos * 6)) & 0x3F; |
| int t0 = bits & 3, t1 = (bits >> 2) & 3, t2 = (bits >> 4) & 3; |
| int lv = ((t2==TRIT_POS)-(t2==TRIT_NEG))*9 + ((t1==TRIT_POS)-(t1==TRIT_NEG))*3 |
| + ((t0==TRIT_POS)-(t0==TRIT_NEG)); |
| acc += lv * __ldg(&x[idx]); |
| } |
| return acc; |
| } |
|
|
| __global__ void k_v9( |
| const uint32_t* __restrict__ pt, const float* __restrict__ sc, |
| const float* __restrict__ x, float* __restrict__ y, |
| int in_f, int out_f, int depth |
| ) { |
| __shared__ float parts[V9R * V9W]; |
| int base = blockIdx.x * V9R; |
| int wid = threadIdx.x / WARP_SIZE, lane = threadIdx.x % WARP_SIZE; |
| int lr = wid / V9W, rw = wid % V9W, row = base + lr; |
| int ng = in_f / GROUP_SIZE; |
| const int w = 13; |
|
|
| int half = lane / 16; |
| int tid_in_group = lane % 16; |
|
|
| float partial = 0.0f; |
| if (row < out_f) { |
| for (int g_pair = rw; g_pair < (ng + 1) / 2; g_pair += V9W) { |
| int g = g_pair * 2 + half; |
| if (g < ng) { |
| float ga = mac_wide_d3(&pt[(row * ng + g) * w], |
| &x[g * GROUP_SIZE], tid_in_group); |
| unsigned mask = half ? 0xFFFF0000u : 0x0000FFFFu; |
| #pragma unroll |
| for (int o = 8; o > 0; o >>= 1) |
| ga += __shfl_down_sync(mask, ga, o); |
| if (tid_in_group == 0) |
| partial += ga * __ldg(&sc[row * ng + g]); |
| } |
| } |
| } |
|
|
| float my_partial = (lane == 0 || lane == 16) ? partial : 0.0f; |
| my_partial += __shfl_xor_sync(0xFFFFFFFF, my_partial, 16); |
| if (lane == 0) parts[wid] = my_partial; |
| __syncthreads(); |
| if (lane == 0 && rw == 0 && row < out_f) { |
| float s = 0; |
| for (int i = 0; i < V9W; i++) s += parts[lr * V9W + i]; |
| y[row] = s; |
| } |
| } |
|
|
| |
| |
| |
|
|
| static void set_l2_persist(void* ptr, size_t bytes) { |
| cudaStreamAttrValue attr; |
| attr.accessPolicyWindow.base_ptr = ptr; |
| attr.accessPolicyWindow.num_bytes = bytes; |
| attr.accessPolicyWindow.hitRatio = 1.0f; |
| attr.accessPolicyWindow.hitProp = cudaAccessPropertyPersisting; |
| attr.accessPolicyWindow.missProp = cudaAccessPropertyStreaming; |
| cudaStreamSetAttribute(0, cudaStreamAttributeAccessPolicyWindow, &attr); |
| } |
|
|
| static void clear_l2_persist() { |
| cudaStreamAttrValue attr; |
| memset(&attr, 0, sizeof(attr)); |
| cudaStreamSetAttribute(0, cudaStreamAttributeAccessPolicyWindow, &attr); |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| #define TRIT_OK 0 |
| #define TRIT_ERR_NULL_PTR -1 |
| #define TRIT_ERR_BAD_DIM -2 |
| #define TRIT_ERR_BAD_GROUP -3 |
| #define TRIT_ERR_BAD_BUFFER -4 |
|
|
| |
| static int g_last_error = TRIT_OK; |
|
|
| |
| |
| static inline int trit_validate_gemv( |
| const void* pt, const void* ws, const void* y, |
| int cols, int rows, int num_groups |
| ) { |
| if (!pt || !ws || !y) { g_last_error = TRIT_ERR_NULL_PTR; return 1; } |
| if (cols <= 0 || rows <= 0 || num_groups <= 0) { g_last_error = TRIT_ERR_BAD_DIM; return 1; } |
| if (cols % GROUP_SIZE != 0) { g_last_error = TRIT_ERR_BAD_DIM; return 1; } |
| if (cols / GROUP_SIZE != num_groups) { g_last_error = TRIT_ERR_BAD_GROUP; return 1; } |
| return 0; |
| } |
|
|
| |
| static inline void trit_capture_launch_status() { |
| cudaError_t e = cudaGetLastError(); |
| g_last_error = (e == cudaSuccess) ? TRIT_OK : (int)e; |
| } |
|
|
| extern "C" { |
|
|
| |
| |
| |
| int trit_gemv_get_last_error() { |
| return g_last_error; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| void trit_gemv_d2_dp4a( |
| const int32_t* pt, const float* ws, |
| const int32_t* xt, const float* xs, |
| float* y, int cols, int rows, int num_groups, |
| int use_l2_persist |
| ) { |
| if (trit_validate_gemv(pt, ws, y, cols, rows, num_groups)) return; |
| if (!xt || !xs) { g_last_error = TRIT_ERR_NULL_PTR; return; } |
| if (use_l2_persist) { |
| set_l2_persist((void*)pt, (size_t)rows * num_groups * 8 * sizeof(int32_t)); |
| } |
| k_v27<<<(rows + V27_RPB - 1) / V27_RPB, V27_BS>>>( |
| (const uint32_t*)pt, ws, (const uint32_t*)xt, xs, y, cols, rows, num_groups); |
| trit_capture_launch_status(); |
| if (use_l2_persist) { |
| clear_l2_persist(); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| void trit_gemv_d3_native( |
| const int32_t* pt, const float* sc, |
| const float* x, float* y, |
| int cols, int rows, int depth |
| ) { |
| if (!pt || !sc || !x || !y) { g_last_error = TRIT_ERR_NULL_PTR; return; } |
| if (cols <= 0 || rows <= 0) { g_last_error = TRIT_ERR_BAD_DIM; return; } |
| if (cols % GROUP_SIZE != 0) { g_last_error = TRIT_ERR_BAD_DIM; return; } |
| if (depth < 1 || depth > 4) { g_last_error = TRIT_ERR_BAD_DIM; return; } |
| k_v9<<<(rows + V9R - 1) / V9R, V9BS>>>( |
| (const uint32_t*)pt, sc, x, y, cols, rows, depth); |
| trit_capture_launch_status(); |
| } |
|
|
| |
| void trit_gemv_d2_bias( |
| const int32_t* pt, const float* ws, |
| const int32_t* xt_e, const int32_t* xt_o, |
| const int32_t* x_bias, const float* xs, |
| float* y, int cols, int rows, int num_groups, |
| int use_l2_persist |
| ) { |
| if (trit_validate_gemv(pt, ws, y, cols, rows, num_groups)) return; |
| if (!xt_e || !xt_o || !x_bias || !xs) { g_last_error = TRIT_ERR_NULL_PTR; return; } |
| if (use_l2_persist) { |
| set_l2_persist((void*)pt, (size_t)rows * num_groups * 8 * sizeof(int32_t)); |
| } |
| k_v29<<<(rows + V29_RPB - 1) / V29_RPB, V29_BS>>>( |
| (const uint32_t*)pt, ws, |
| (const uint32_t*)xt_e, (const uint32_t*)xt_o, |
| (const int*)x_bias, xs, |
| y, cols, rows, num_groups); |
| trit_capture_launch_status(); |
| if (use_l2_persist) { |
| clear_l2_persist(); |
| } |
| } |
|
|
| |
| |
| |
| void trit_gemv_d2_fast( |
| const int32_t* pt, const float* ws, |
| const int32_t* xt_e, const int32_t* xt_o, const float* xs, |
| float* y, int cols, int rows, int num_groups, |
| int use_l2_persist |
| ) { |
| if (trit_validate_gemv(pt, ws, y, cols, rows, num_groups)) return; |
| if (!xt_e || !xt_o || !xs) { g_last_error = TRIT_ERR_NULL_PTR; return; } |
| if (use_l2_persist) { |
| set_l2_persist((void*)pt, (size_t)rows * num_groups * 8 * sizeof(int32_t)); |
| } |
| k_v28<<<(rows + V28_RPB - 1) / V28_RPB, V28_BS>>>( |
| (const uint32_t*)pt, ws, |
| (const uint32_t*)xt_e, (const uint32_t*)xt_o, xs, |
| y, cols, rows, num_groups); |
| trit_capture_launch_status(); |
| if (use_l2_persist) { |
| clear_l2_persist(); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| #define V21F_RPB 4 |
| #define V21F_BS (V21F_RPB * WARP_SIZE) |
|
|
| __global__ void k_v21f_standalone( |
| const uint32_t* __restrict__ wt, |
| const float* __restrict__ ws, |
| const uint32_t* __restrict__ xt, |
| const float* __restrict__ xs, |
| float* __restrict__ y, |
| int cols, int rows, int num_groups |
| ) { |
| int wid = threadIdx.x / WARP_SIZE; |
| int lane = threadIdx.x % WARP_SIZE; |
| int row = blockIdx.x * V21F_RPB + wid; |
| if (row >= rows) return; |
|
|
| const uint32_t* row_w = &wt[row * num_groups * 16]; |
| const float* row_ws = &ws[row * num_groups]; |
| float acc = 0.0f; |
| int total_words = num_groups * 16; |
|
|
| for (int base = 0; base < total_words; base += WARP_SIZE) { |
| int w = base + lane; |
| if (w < total_words) { |
| uint32_t w_word = __ldg(&row_w[w]); |
| uint32_t x_word = __ldg(&xt[w]); |
| int dp = __dp4a((int)w_word, (int)x_word, 0); |
| int g = w >> 4; |
| acc += (float)dp * __ldg(&row_ws[g]) * __ldg(&xs[g]); |
| } |
| } |
|
|
| #pragma unroll |
| for (int o = 16; o > 0; o >>= 1) |
| acc += __shfl_down_sync(0xFFFFFFFF, acc, o); |
|
|
| if (lane == 0) y[row] = acc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #define D3H_RPB 16 |
| #define D3H_WPG 16 |
| #define D3H_BS (D3H_RPB * WARP_SIZE) |
|
|
| __global__ void k_d3_hardened( |
| const uint32_t* __restrict__ wt, |
| const float* __restrict__ ws, |
| const uint32_t* __restrict__ xt, |
| const float* __restrict__ xs, |
| float* __restrict__ y, |
| int cols, int rows, int num_groups |
| ) { |
| int wid = threadIdx.x / WARP_SIZE; |
| int lane = threadIdx.x % WARP_SIZE; |
| int row = blockIdx.x * D3H_RPB + wid; |
| if (row >= rows) return; |
|
|
| const uint32_t* row_w = &wt[row * num_groups * D3H_WPG]; |
| const float* row_ws = &ws[row * num_groups]; |
|
|
| float acc = 0.0f; |
| int total_words = num_groups * D3H_WPG; |
|
|
| for (int base = 0; base < total_words; base += WARP_SIZE) { |
| int w = base + lane; |
| if (w < total_words) { |
| |
| uint32_t w_word = __ldg(&row_w[w]); |
| uint32_t x_word = __ldg(&xt[w]); |
|
|
| |
| int dp = __dp4a((int)w_word, (int)x_word, 0); |
|
|
| |
| int g = w >> 4; |
|
|
| |
| acc += (float)dp * __ldg(&row_ws[g]) * __ldg(&xs[g]); |
| } |
| } |
|
|
| |
| #pragma unroll |
| for (int o = 16; o > 0; o >>= 1) |
| acc += __shfl_down_sync(0xFFFFFFFF, acc, o); |
|
|
| if (lane == 0) y[row] = acc; |
| } |
|
|
| |
| void trit_gemv_d3_int8_dp4a( |
| const int32_t* wt, const float* ws, |
| const int32_t* xt, const float* xs, |
| float* y, int cols, int rows, int num_groups, |
| int use_l2_persist |
| ) { |
| if (trit_validate_gemv(wt, ws, y, cols, rows, num_groups)) return; |
| if (!xt || !xs) { g_last_error = TRIT_ERR_NULL_PTR; return; } |
| if (use_l2_persist) { |
| set_l2_persist((void*)wt, (size_t)rows * num_groups * 16 * sizeof(int32_t)); |
| } |
| k_d3_hardened<<<(rows + D3H_RPB - 1) / D3H_RPB, D3H_BS>>>( |
| (const uint32_t*)wt, ws, (const uint32_t*)xt, xs, y, cols, rows, num_groups); |
| trit_capture_launch_status(); |
| if (use_l2_persist) { |
| clear_l2_persist(); |
| } |
| } |
|
|
| |
| void trit_gemv_pipeline_bench( |
| const int32_t* pt, const float* ws, |
| const int32_t* xt_e, const int32_t* xt_o, const float* xs, |
| float* y, int cols, int rows, int num_groups, |
| int n_repeats, int use_l2_persist |
| ) { |
| if (trit_validate_gemv(pt, ws, y, cols, rows, num_groups)) return; |
| if (!xt_e || !xt_o || !xs || n_repeats <= 0) { g_last_error = TRIT_ERR_NULL_PTR; return; } |
| if (use_l2_persist) { |
| set_l2_persist((void*)pt, (size_t)rows * num_groups * 8 * sizeof(int32_t)); |
| } |
| |
| |
| for (int i = 0; i < n_repeats; i++) { |
| k_v28<<<(rows + V28_RPB - 1) / V28_RPB, V28_BS>>>( |
| (const uint32_t*)pt, ws, |
| (const uint32_t*)xt_e, (const uint32_t*)xt_o, xs, |
| y, cols, rows, num_groups); |
| } |
| trit_capture_launch_status(); |
| if (use_l2_persist) { |
| clear_l2_persist(); |
| } |
| } |
|
|
| |
| int get_l2_cache_bytes() { |
| cudaDeviceProp prop; |
| cudaGetDeviceProperties(&prop, 0); |
| return prop.l2CacheSize; |
| } |
|
|
| |
| |
| void get_gpu_name(char* buf, int buflen) { |
| if (!buf || buflen <= 0) { g_last_error = TRIT_ERR_BAD_BUFFER; return; } |
| cudaDeviceProp prop; |
| cudaError_t e = cudaGetDeviceProperties(&prop, 0); |
| if (e != cudaSuccess) { g_last_error = (int)e; buf[0] = '\0'; return; } |
| strncpy(buf, prop.name, buflen - 1); |
| buf[buflen - 1] = '\0'; |
| } |
|
|
| |
| void cuda_sync() { |
| cudaDeviceSynchronize(); |
| } |
|
|
| } |
|
|