Buckets:
| #version 450 | |
| #extension GL_EXT_shader_explicit_arithmetic_types_int32 : require | |
| #include "mul_mat_vec_base.glsl" | |
| #include "dequant_funcs.glsl" | |
| layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in; | |
| #if !defined(DATA_A_F32) && !defined(DATA_A_F16) && !defined(DATA_A_BF16) | |
| #define K_PER_ITER 8 | |
| #else | |
| #define K_PER_ITER 4 | |
| #endif | |
| uint a_offset, b_offset, d_offset, y_offset; | |
| vec4 load_b(const uint j, const uint iybs, const uint iqs, const bool lastiter, out bool OOB_y, out bool OOB_z, out bool OOB_w) { | |
| // Check if the latter elements are OOB, and don't fetch B or accumulate it. | |
| OOB_y = lastiter && (iybs + iqs + y_offset >= p.ncols); | |
| OOB_z = lastiter && (iybs + iqs + y_offset*2 >= p.ncols); | |
| OOB_w = lastiter && (iybs + iqs + y_offset*3 >= p.ncols); | |
| if (!OOB_w) { | |
| return vec4(FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs]), | |
| FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs + y_offset]), | |
| FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs + y_offset*2]), | |
| FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs + y_offset*3])); | |
| } else if (!OOB_z) { | |
| return vec4(FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs]), | |
| FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs + y_offset]), | |
| FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs + y_offset*2]), | |
| 0); | |
| } else if (!OOB_y) { | |
| return vec4(FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs]), | |
| FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs + y_offset]), | |
| 0, 0); | |
| } else { | |
| return vec4(FLOAT_TYPE(data_b[j*p.batch_stride_b + b_offset + iybs + iqs]), | |
| 0, 0, 0); | |
| } | |
| } | |
| void iter(inout FLOAT_TYPE temp[NUM_COLS][NUM_ROWS], const uint first_row, const uint num_rows, const uint tid, const uint i, bool lastiter) | |
| { | |
| [[unroll]] for (uint j = 0; j < NUM_COLS; ++j) { | |
| const uint col = i*BLOCK_SIZE + K_PER_ITER*tid; | |
| const uint iqs = (col%QUANT_K)/QUANT_R; // quant index | |
| const uint iybs = col - col%QUANT_K; // y block start index | |
| #if K_PER_ITER == 8 | |
| #if QUANT_R == 2 | |
| // Note that we end up fetching bogus elements here, but its fine as they'll be | |
| // within an accessible block. | |
| const vec4 bv02 = vec4(data_b_v4[(j*p.batch_stride_b + b_offset + iybs + iqs) / 4]); | |
| const vec4 bv13 = vec4(data_b_v4[(j*p.batch_stride_b + b_offset + iybs + iqs + y_offset) / 4]); | |
| const vec4 bv0 = vec4(bv02.x, bv13.x, bv02.y, bv13.y); | |
| const vec4 bv1 = vec4(bv02.z, bv13.z, bv02.w, bv13.w); | |
| #else | |
| const vec4 bv0 = vec4(data_b_v4[(j*p.batch_stride_b + b_offset + iybs + iqs) / 4]); | |
| const vec4 bv1 = vec4(data_b_v4[(j*p.batch_stride_b + b_offset + iybs + iqs) / 4 + 1]); | |
| #endif | |
| #else | |
| bool OOB_y; | |
| bool OOB_z; | |
| bool OOB_w; | |
| const vec4 b = load_b(j, iybs, iqs, lastiter, OOB_y, OOB_z, OOB_w); | |
| #endif | |
| uint ibi = first_row*p.ncols; | |
| [[unroll]] for (uint n = 0; n < num_rows; ++n) { | |
| const uint ib = (ibi + col)/QUANT_K; // block index | |
| ibi += p.ncols; | |
| #if K_PER_ITER == 8 | |
| vec4 v = dequantize4(ib, iqs, a_offset); | |
| vec4 v2 = dequantize4(ib, iqs+(4/QUANT_R), a_offset); | |
| const vec2 dm = get_dm(ib, a_offset); | |
| if (dm.y != 0) { // quant has min component | |
| v = v * dm.x + dm.y; | |
| v2 = v2 * dm.x + dm.y; | |
| } | |
| // matrix multiplication | |
| FLOAT_TYPE rowtmp = dot(bv0, v); | |
| rowtmp += dot(bv1, v2); | |
| if (dm.y == 0) | |
| rowtmp *= dm.x; | |
| temp[j][n] += rowtmp; | |
| #else | |
| if (!OOB_w) { | |
| const vec4 v = dequantize4(ib, iqs, a_offset); | |
| temp[j][n] += dot(v, b); | |
| } else if (!OOB_z) { | |
| const vec2 v0 = dequantize(ib, iqs, a_offset); | |
| const FLOAT_TYPE v1 = dequantize1(ib + 2/QUANT_R, iqs, a_offset); | |
| const vec3 v = vec3(v0.x, v0.y, v1); | |
| const vec3 b0 = vec3(b.x, b.y, b.z); | |
| temp[j][n] += dot(v, b0); | |
| } else if (!OOB_y) { | |
| const vec2 v0 = dequantize(ib, iqs, a_offset); | |
| const vec2 b0 = vec2(b.x, b.y); | |
| temp[j][n] += dot(v0, b0); | |
| } else { | |
| const FLOAT_TYPE v = dequantize1(ib, iqs, a_offset); | |
| temp[j][n] = fma(v, b.x, temp[j][n]); | |
| } | |
| #endif | |
| } | |
| } | |
| } | |
| #if defined(DATA_A_F32) || defined(DATA_A_F16) || defined(DATA_A_BF16) | |
| void iter_aligned_nonquant(inout FLOAT_TYPE temp[NUM_COLS][NUM_ROWS], const uint first_row, const uint num_rows, const uint tid, const uint i) | |
| { | |
| [[unroll]] for (uint j = 0; j < NUM_COLS; ++j) { | |
| const uint col = i*BLOCK_SIZE + K_PER_ITER*tid; | |
| const uint iqs = 0; // quant index | |
| const uint iybs = col; // y block start index | |
| const vec4 b = data_b_v4[(j*p.batch_stride_b + b_offset + iybs + iqs) / 4]; | |
| uint ibi = first_row*p.ncols; | |
| [[unroll]] for (uint n = 0; n < num_rows; ++n) { | |
| const uint ib = (ibi + col)/QUANT_K; // block index | |
| ibi += p.ncols; | |
| const vec4 v = dequantize4_2aligned(ib, iqs, a_offset); | |
| // matrix multiplication | |
| temp[j][n] += dot(v, b); | |
| } | |
| } | |
| } | |
| #endif | |
| void compute_outputs(const uint32_t first_row, const uint32_t num_rows) { | |
| const uint tid = gl_LocalInvocationID.x; | |
| get_offsets(a_offset, b_offset, d_offset); | |
| const bool is_aligned_nonquant = | |
| p.batch_stride_b % 4 == 0 && b_offset % 4 == 0 && | |
| p.ncols % 4 == 0 && BLOCK_SIZE % 4 == 0 && | |
| K_PER_ITER == 4; | |
| y_offset = QUANT_R == 1 ? 1 : QUANT_K/2; | |
| FLOAT_TYPE temp[NUM_COLS][NUM_ROWS]; | |
| [[unroll]] for (uint j = 0; j < NUM_COLS; ++j) { | |
| [[unroll]] for (uint i = 0; i < NUM_ROWS; ++i) { | |
| temp[j][i] = FLOAT_TYPE(0); | |
| } | |
| } | |
| uint num_iters = p.ncols / (K_PER_ITER * BLOCK_SIZE); | |
| if (num_iters * K_PER_ITER * BLOCK_SIZE + K_PER_ITER*tid < p.ncols) { | |
| num_iters++; | |
| } | |
| int unroll_count = 4; | |
| uint unrolled_iters = num_iters & ~(unroll_count - 1); | |
| uint i = 0; | |
| #if K_PER_ITER == 4 | |
| // If the K dimension is odd, we need lastiter==true on the last iteration | |
| // so OOB is computed correctly. Skip some unrolling to make that happen. | |
| if ((p.ncols & 3) != 0 && | |
| unrolled_iters == num_iters && | |
| unrolled_iters > 0) { | |
| unrolled_iters -= unroll_count; | |
| } | |
| if (is_aligned_nonquant) { | |
| while (i < unrolled_iters) { | |
| // Manually partially unroll the loop | |
| [[unroll]] for (uint k = 0; k < unroll_count; ++k) { | |
| iter_aligned_nonquant(temp, first_row, num_rows, tid, i*K_PER_ITER); | |
| i++; | |
| } | |
| } | |
| } else { | |
| #endif | |
| while (i < unrolled_iters) { | |
| // Manually partially unroll the loop | |
| [[unroll]] for (uint k = 0; k < unroll_count; ++k) { | |
| iter(temp, first_row, num_rows, tid, i*K_PER_ITER, false); | |
| i++; | |
| } | |
| } | |
| #if K_PER_ITER == 4 | |
| } | |
| #endif | |
| unroll_count = 2; | |
| unrolled_iters = num_iters & ~(unroll_count - 1); | |
| #if K_PER_ITER == 4 | |
| if ((p.ncols & 3) != 0 && | |
| unrolled_iters == num_iters && | |
| unrolled_iters > 0) { | |
| unrolled_iters -= unroll_count; | |
| } | |
| if (is_aligned_nonquant) { | |
| while (i < unrolled_iters && is_aligned_nonquant) { | |
| // Manually partially unroll the loop | |
| [[unroll]] for (uint k = 0; k < unroll_count; ++k) { | |
| iter_aligned_nonquant(temp, first_row, num_rows, tid, i*K_PER_ITER); | |
| i++; | |
| } | |
| } | |
| } else { | |
| #endif | |
| while (i < unrolled_iters) { | |
| // Manually partially unroll the loop | |
| [[unroll]] for (uint k = 0; k < unroll_count; ++k) { | |
| iter(temp, first_row, num_rows, tid, i*K_PER_ITER, false); | |
| i++; | |
| } | |
| } | |
| #if K_PER_ITER == 4 | |
| } | |
| #endif | |
| #if K_PER_ITER == 4 | |
| if (is_aligned_nonquant) { | |
| while (i < num_iters) { | |
| iter_aligned_nonquant(temp, first_row, num_rows, tid, i*K_PER_ITER); | |
| i++; | |
| } | |
| } else { | |
| #endif | |
| while (i < num_iters) { | |
| iter(temp, first_row, num_rows, tid, i*K_PER_ITER, true); | |
| i++; | |
| } | |
| #if K_PER_ITER == 4 | |
| } | |
| #endif | |
| reduce_result(temp, d_offset, first_row, num_rows, tid); | |
| } | |
| void main() { | |
| const uint first_row = NUM_ROWS * (gl_WorkGroupID.x + gl_NumWorkGroups.x * gl_WorkGroupID.z); | |
| #ifdef NEEDS_INIT_IQ_SHMEM | |
| init_iq_shmem(gl_WorkGroupSize); | |
| #endif | |
| // do NUM_ROWS at a time, unless there aren't enough remaining rows | |
| if (first_row + NUM_ROWS <= p.stride_d) { | |
| compute_outputs(first_row, NUM_ROWS); | |
| } else { | |
| if (first_row >= p.stride_d) { | |
| return; | |
| } | |
| compute_outputs(first_row, min(NUM_ROWS, p.stride_d - first_row)); | |
| } | |
| } | |
Xet Storage Details
- Size:
- 9.11 kB
- Xet hash:
- 1bdbaa9374fdf5a1a4c3cb2ea62884f6eababf8c1628832f2428dbe37534cb90
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.