Repository navigation
vulkan : dequant q8_0 KV once in coopmat1 #25494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
484ad9b
b805834
d7f6bce
643e95e
c3b3bb9
dd39add
f9f99bf
500c887
b890946
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -790,6 +790,7 @@ struct vk_device_struct { | |
| vk_pipeline pipeline_quantize_q8_1_x4; | ||
|
|
||
| vk_pipeline pipeline_dequant[GGML_TYPE_COUNT]; | ||
| vk_pipeline pipeline_dequant_transpose[GGML_TYPE_COUNT]; // fused dequant+transpose for FA quant-KV | ||
| vk_pipeline pipeline_dequant_mul_mat_vec_f32_f32[DMMV_WG_SIZE_COUNT][GGML_TYPE_COUNT][mul_mat_vec_max_cols]; | ||
| vk_pipeline pipeline_dequant_mul_mat_vec_f16_f32[DMMV_WG_SIZE_COUNT][GGML_TYPE_COUNT][mul_mat_vec_max_cols]; | ||
| vk_pipeline pipeline_dequant_mul_mat_vec_id_f32[DMMV_WG_SIZE_COUNT][GGML_TYPE_COUNT]; | ||
|
|
@@ -5000,6 +5001,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) { | |
| ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q5_0], "dequant_q5_0", dequant_q5_0_len, dequant_q5_0_data, "main", 2, 5 * sizeof(uint32_t), {256 * 16, 1, 1}, {}, 1); | ||
| ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q5_1], "dequant_q5_1", dequant_q5_1_len, dequant_q5_1_data, "main", 2, 5 * sizeof(uint32_t), {256 * 16, 1, 1}, {}, 1); | ||
| ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q8_0], "dequant_q8_0", dequant_q8_0_len, dequant_q8_0_data, "main", 2, 5 * sizeof(uint32_t), {256 * 16, 1, 1}, {}, 1); | ||
| ggml_vk_create_pipeline(device, device->pipeline_dequant_transpose[GGML_TYPE_Q8_0], "dequant_q8_0_transpose", dequant_q8_0_transpose_len, dequant_q8_0_transpose_data, "main", 2, 5 * sizeof(uint32_t), {256 * 16, 1, 1}, {}, 1); | ||
| ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q2_K], "dequant_q2_k", dequant_q2_k_len, dequant_q2_k_data, "main", 2, 5 * sizeof(uint32_t), {256 * 64, 1, 1}, {}, 1); | ||
| ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q3_K], "dequant_q3_k", dequant_q3_k_len, dequant_q3_k_data, "main", 2, 5 * sizeof(uint32_t), {256 * 64, 1, 1}, {}, 1); | ||
| ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q4_K], "dequant_q4_k", dequant_q4_k_len, dequant_q4_k_data, "main", 2, 5 * sizeof(uint32_t), {256 * 32, 1, 1}, {}, 1); | ||
|
|
@@ -10258,9 +10260,25 @@ static void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx | |
|
|
||
| const bool f32acc = !ctx->device->fp16 || dst->op_params[3] == GGML_PREC_F32 || k->type == GGML_TYPE_BF16; | ||
|
|
||
| // dequant K/V once into an f16 scratch, reordered KV layout so FA can read without a stride | ||
| const bool k_quant = k->type != GGML_TYPE_F16 && k->type != GGML_TYPE_BF16 && k->type != GGML_TYPE_F32; | ||
| const bool v_quant = v->type != GGML_TYPE_F16 && v->type != GGML_TYPE_BF16 && v->type != GGML_TYPE_F32; | ||
| const bool use_dequant_kv = k_quant && v_quant && neq1 >= 64 && | ||
| k->nb[0] == ggml_type_size(k->type) && v->nb[0] == ggml_type_size(v->type) && | ||
| k->nb[1] >= k->nb[2] && v->nb[1] >= v->nb[2] && | ||
| ggml_is_contiguously_allocated(k) && ggml_is_contiguously_allocated(v) && | ||
| (uint64_t)ggml_nelements(k) * sizeof(ggml_fp16_t) <= ctx->device->properties.limits.maxStorageBufferRange && | ||
| (uint64_t)ggml_nelements(v) * sizeof(ggml_fp16_t) <= ctx->device->properties.limits.maxStorageBufferRange && | ||
| ctx->device->pipeline_dequant_transpose[k->type] != nullptr && | ||
| ctx->device->pipeline_dequant_transpose[v->type] != nullptr && | ||
| // coopmat2 already decodes quant K/V during the matrix load, so the f16 scratch is pure overhead there | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think coopmat2 does anything different here. This comment is just speculation. It should just say that the coopmat2 path does not benefit from this feature. |
||
| !ctx->device->coopmat2; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please limit the feature to non-Intel and Intel+coopmat support here. |
||
| const ggml_type k_type_eff = use_dequant_kv ? GGML_TYPE_F16 : k->type; | ||
| const ggml_type v_type_eff = use_dequant_kv ? GGML_TYPE_F16 : v->type; | ||
|
|
||
| // For scalar/coopmat1 FA, we can use the "large" size to accommodate qga. | ||
| // For coopmat2 FA, we always use the small size (which is still pretty large for gqa). | ||
| vk_fa_tuning_params tuning_params = get_fa_tuning_params(ctx->device, HSK, HSV, 512, KV, k->type, v->type, f32acc); | ||
| vk_fa_tuning_params tuning_params = get_fa_tuning_params(ctx->device, HSK, HSV, 512, KV, k_type_eff, v_type_eff, f32acc); | ||
| const uint32_t max_gqa = std::min(tuning_params.block_rows, 32u); | ||
|
|
||
| if (N <= 8 && qk_ratio > 1 && qk_ratio <= max_gqa && | ||
|
|
@@ -10273,7 +10291,7 @@ static void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx | |
| workgroups_y /= gqa_ratio; | ||
| } | ||
|
|
||
| tuning_params = get_fa_tuning_params(ctx->device, HSK, HSV, N, KV, k->type, v->type, f32acc); | ||
| tuning_params = get_fa_tuning_params(ctx->device, HSK, HSV, N, KV, k_type_eff, v_type_eff, f32acc); | ||
|
|
||
| const uint32_t q_stride = (uint32_t)(nbq1 / ggml_type_size(q->type)); | ||
| uint32_t k_stride = (uint32_t)(nbk1 / ggml_type_size(k->type)); | ||
|
|
@@ -10287,6 +10305,17 @@ static void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx | |
| v_stride /= 4; | ||
| } | ||
|
|
||
| uint32_t nbk2_eff = (uint32_t)nbk2, nbk3_eff = (uint32_t)nbk3; | ||
| uint32_t nbv2_eff = (uint32_t)nbv2, nbv3_eff = (uint32_t)nbv3; | ||
| if (use_dequant_kv) { | ||
| k_stride = HSK; | ||
| v_stride = HSV; | ||
| nbk2_eff = (uint32_t)((uint64_t)HSK * KV * sizeof(ggml_fp16_t)); | ||
| nbk3_eff = (uint32_t)((uint64_t)HSK * KV * nek2 * sizeof(ggml_fp16_t)); | ||
| nbv2_eff = (uint32_t)((uint64_t)HSV * KV * sizeof(ggml_fp16_t)); | ||
| nbv3_eff = (uint32_t)((uint64_t)HSV * KV * nev2 * sizeof(ggml_fp16_t)); | ||
| } | ||
|
|
||
| const uint32_t alignment = tuning_params.block_cols; | ||
| bool aligned = (KV % alignment) == 0 && | ||
| // the "aligned" shader variant will forcibly align strides, for performance | ||
|
|
@@ -10313,7 +10342,7 @@ static void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx | |
| bool use_mask_opt = mask && nem1 >= 32 && nem0 * nem1 > 32768 && nem0 >= tuning_params.block_cols * 16 | ||
| && (ctx->device->architecture != vk_device_architecture::AMD_GCN || HSK > 256 || HSV > 256); | ||
| vk_fa_pipeline_state fa_pipeline_state = get_fa_pipeline_state(ctx->device, tuning_params, HSK, HSV, aligned, f32acc, | ||
| mask != nullptr, use_mask_opt, logit_softcap != 0, k->type, v->type); | ||
| mask != nullptr, use_mask_opt, logit_softcap != 0, k_type_eff, v_type_eff); | ||
|
|
||
| vk_pipeline pipeline = nullptr; | ||
|
|
||
|
|
@@ -10417,6 +10446,35 @@ static void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx | |
| vk_subbuffer sinks_buf = sinks ? ggml_vk_tensor_subbuffer(ctx, sinks) : q_buf; | ||
| vk_subbuffer mask_opt_buf = use_mask_opt ? ggml_vk_subbuffer(ctx, ctx->prealloc_y, 0) : q_buf; | ||
|
|
||
| if (use_dequant_kv) { | ||
| const uint64_t fp = sizeof(ggml_fp16_t); | ||
| const uint64_t k_f16_sz = (uint64_t)ggml_nelements(k) * fp; | ||
| const uint64_t v_f16_sz = (uint64_t)ggml_nelements(v) * fp; | ||
| if (ctx->prealloc_size_x < k_f16_sz + v_f16_sz) { | ||
| ctx->prealloc_size_x = k_f16_sz + v_f16_sz; | ||
| ggml_vk_preallocate_buffers(ctx, subctx); | ||
| } | ||
| vk_pipeline tr_k = ctx->device->pipeline_dequant_transpose[k->type]; | ||
| vk_pipeline tr_v = ctx->device->pipeline_dequant_transpose[v->type]; | ||
| ggml_pipeline_request_descriptor_sets(ctx, tr_k, 1); | ||
| ggml_pipeline_request_descriptor_sets(ctx, tr_v, 1); | ||
| if (ctx->prealloc_x_need_sync) { | ||
| ggml_vk_sync_buffers(ctx, subctx); | ||
| } | ||
| vk_subbuffer k_dst = vk_subbuffer{ ctx->prealloc_x, 0, k_f16_sz }; | ||
| vk_subbuffer v_dst = vk_subbuffer{ ctx->prealloc_x, k_f16_sz, v_f16_sz }; | ||
| const uint32_t k_nel = (uint32_t)ggml_nelements(k); | ||
| const uint32_t v_nel = (uint32_t)ggml_nelements(v); | ||
| { const std::vector<uint32_t> pc = { (uint32_t)HSK, (uint32_t)nek2, (uint32_t)KV, 0, k_nel }; | ||
| ggml_vk_dispatch_pipeline(ctx, subctx, tr_k, { k_buf, k_dst }, pc, { k_nel, 1, 1 }); } | ||
| { const std::vector<uint32_t> pc = { (uint32_t)HSV, (uint32_t)nev2, (uint32_t)KV, 0, v_nel }; | ||
| ggml_vk_dispatch_pipeline(ctx, subctx, tr_v, { v_buf, v_dst }, pc, { v_nel, 1, 1 }); } | ||
| ggml_vk_sync_buffers(ctx, subctx); | ||
| ctx->prealloc_x_need_sync = true; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be set at the end of the function, since the contents of prealloc_x are used in the FA dispatch. Otherwise it can be clobbered in the next ggml_vk_sync_buffers. (Codex found this one) |
||
| k_buf = k_dst; | ||
| v_buf = v_dst; | ||
| } | ||
|
|
||
| uint32_t mask_n_head_log2 = ((sinks != nullptr) << 24) | n_head_log2; | ||
|
|
||
| if (use_mask_opt) | ||
|
|
@@ -10446,8 +10504,8 @@ static void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx | |
| (uint32_t)nev2, (uint32_t)nev3, | ||
| nem1, nem2, nem3, | ||
| q_stride, (uint32_t)nbq2, (uint32_t)nbq3, | ||
| k_stride, (uint32_t)nbk2, (uint32_t)nbk3, | ||
| v_stride, (uint32_t)nbv2, (uint32_t)nbv3, | ||
| k_stride, nbk2_eff, nbk3_eff, | ||
| v_stride, nbv2_eff, nbv3_eff, | ||
| scale, max_bias, logit_softcap, | ||
| mask_n_head_log2, m0, m1, | ||
| gqa_ratio, split_kv, split_k }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the shader is assuming a specific permutation, but this check is looser than that.