vulkan: use spec constant for matrix matrix multiplication A-type - #25773
vulkan: use spec constant for matrix matrix multiplication A-type#257730cc4m wants to merge 8 commits into
Conversation
|
I had experimented with something like this and ended up with one spirv for all the legacy and K quants and then separate spirvs for all the iq quants. That resolves the shared memory issue and probably isn't a ton of bloat because those spirvs are dominated by the table size anyway. Fwiw, even if our compiler dead code eliminated the shared memory arrays, they would still count against the spec limit. |
a619971 to
496b099
Compare
|
I think the shared memory arrays with length 1 could run into this compiler bug again #24924. |
|
I don't see these CI failures locally, not sure what is causing them. Is that the compiler bug? |
I also can't reproduce the cm1 failures locally. It's conceivable it's the Turing-only bug. I think it's more likely to happen if you use more fp16 math, is there any place you've changed fp32 math to fp16? |
|
No, the PR should not contain any functional changes, it's all just restructuring. The running code in the end should be basically identical. |
cbf3468 to
001600c
Compare
001600c to
197898e
Compare
|
@ggerganov The Nvidia-CM pipeline is timing out in test-backend-ops, I haven't seen that before. Is that expected? Is it possible to increase the timeout? |
|
Hm, not sure - I can't see a reason for the increased time. It almost doubled than usual for this job (~15 mins). Also, looking at the most recent runs in https://github.com/ggml-org/llama.cpp/actions/workflows/build-self-hosted.yml it seems to be fluctuating between slow/fast without a clear point where it started happening. Let's keep monitoring. These workflows are currently running on DGX Spark that I self-host. We are already looking for ways to migrate this to some more stable and scalable infrastructure, but don't have ETA atm. |
|
Did it recently switch from T4 to the Spark? That would explain why it now passed some tests that were failing earlier due (likely to the Turing driver bug. |
|
The DGX Spark has been running these and other jobs for about 3 months now (added on May 26). The T4 runners are also running - whoever runner picks of the job first will execute it. |
Maybe this change effects enough shaders that it get no benefit from the shader disk cache from previous runs? If so, a rerun might be enough to make it complete. |
|
I reran it multiple times and it keeps failing, so I don't think that's it. I'll try to reproduce locally. |
|
It consistently happens in the CI, but I cannot reproduce it locally. I get worst case 7 minutes on my DGX Spark, with caching just a little bit more than 3 minutes. I'm not sure what is going on there. |
|
Here are the logs of one slow and one fast run: One thing that draws attention is that in the slow run, the cmake builds at the start are quite a bit slower. F.ex: |
Is this 7 minutes for the entire |
|
Just for |
|
The entire CI script went from 22:46 on master uncached to 12:09 cached, with the PR from 13:14 uncached to 7:44 cached, on my Spark. That isn't consistent with the CI either. |
|
I just tried manually - on both my DGX Sparks, this command deadlocks/blocks with Vulkan on this branch: GGML_VK_DISABLE_COOPMAT2=1 ./bin/test-backend-ops -o MUL_MATThere is a suspicious error though at the start about incompatible driver:
Edit: the same command works fine on |
|
Yes, it completes without issue. It's suspicious that your driver reports the device as The driver warning is a mesa adreno Vulkan driver artifact, you can ignore it. |
|
Here is DetailsHere is the DetailsThe jobs also have this info at the start of the logs: https://github.com/ggml-org/llama.cpp/actions/runs/32224960472/job/97381646994?pr=25773#step:3:54. For example this job, runs on |
|
Sorry, I made a mistake testing, my environment wasn't set up correctly and I didn't notice. I can actually reproduce it, I'll look into it. |
|
I excluded LUT quants from the new unified shader, which seems to avoid whatever bug Nvidia was hitting with coopmat1. I'll clean up the code, then this should be ready for review. |
df88488 to
19e4a29
Compare
19e4a29 to
ce8cadf
Compare
vulkan: use map for mul_mm shapes cleanup fix indentation fix cm2 and shmem init fix cm2 spec constants fix cm2 bindings consolidate shmem tables and reduce size by type spec constant fix compiler warning fix missing Q2_0 type fix unused warning when integer dot glslc support is missing use minimal shmem size 8 instead of 1 to workaround cm2 compiler bug fix missing Q2_0 type in cm2 matmul fix types
c212193 to
a8f38a9
Compare
|
The CI does indeed time out if I change something and it has recompile the matmul shaders. But otherwise it seems stable now. This PR is gonna clash with #27952, but I can rebase whichever comes last. @jeffbolznv Let me know if you have concerns or more ideas for this branch, I think overall it's a good change to get rid of the static s, m, l * un/aligned values, and should allow device-specific tuning without complicating the code further. I'm not sure why I couldn't get coopmat2 q4_k/q5_k unified, would be cleaner, but regressed on my 3090, while my GB10 was unaffected. But even without that getting rid of most non-LUT quant copies of mul_mm should be good. Master ggml-vulkan.so library is 55MB, with this PR it becomes 42MB. |
| const uint sign8 = sign7 | (bitCount(sign7) << 7); | ||
| const uint sign = sign8 >> (iqs % 8); | ||
| const u8vec4 grid = unpack8(iq2xxs_grid[qs][(iqs % 8) / 4] >> (8 * (iqs % 4))); | ||
| const u8vec4 grid = unpack8(iq2_grid[qs][(iqs % 8) / 4] >> (8 * (iqs % 4))); |
There was a problem hiding this comment.
What's the motivation for renaming the LUTs?
There was a problem hiding this comment.
Originally it was to consolidate them as much as possible for the unified shader. Now it serves no purpose anymore, I think. I'll clean it up.
| #define DECODEFUNCA , dequantFuncA | ||
| #endif | ||
| #else | ||
| // Dispatch coopMatLoadTensorNV to the right decode function |
There was a problem hiding this comment.
I think it should be possible to avoid all this complexity and load from a uint8_t array, you would just need to scale pos_a.
There was a problem hiding this comment.
True, I missed that. Done.
| create_mm_pipelines({TYPE, GGML_TYPE_F16, false, true}, tc, "matmul_" #tstr "_f16_f16acc", matmul_##tstr##_f16_f16acc_cm2_len, matmul_##tstr##_f16_f16acc_cm2_data, sizeof(vk_mat_mat_push_constants), 3, cm2_spec, true); \ | ||
| create_mm_pipelines({TYPE, GGML_TYPE_F16, false, false}, tc, "matmul_" #tstr "_f16", matmul_##tstr##_f16_cm2_len, matmul_##tstr##_f16_cm2_data, sizeof(vk_mat_mat_push_constants), 3, cm2_spec, true); \ | ||
| } } | ||
| FOR_EACH_LUT_TYPE(X_CM2) |
There was a problem hiding this comment.
An issue reported by codex:
- [P1] Generic FP4 shaders claim the OCP pipeline keys first - ggml/src/ggml-vulkan/ggml-vulkan.cpp:4774. FOR_EACH_LUT_TYPE registers MXFP4/NVFP4 before the OCP-specific block
registers the same keys. Because pipeline objects are reused and the lazy loader retains the first SPIR-V assigned, OCP-capable devices compile the generic shader and never
use the accelerated OCP shader. This affects CM1 and CM2, including MUL_MAT_ID. Restore the previous if (device->ocp_fp4) ... else ... selection or exclude FP4 from the
generic registration on OCP devices.
I have not attempted to review this file yet, but I read through most of the shader changes.
There was a problem hiding this comment.
Yes, that was correct. Should be fixed.
|
This sometimes happens on Nvidia coopmat2/coopmat1, which is worrying: I can't reproduce it reliably. |
jeffbolznv
left a comment
There was a problem hiding this comment.
I read through ggml-vulkan.cpp this time but not in enough detail to fully follow it. It's pretty dense, but then again the previous code was, too. I've run the backend tests, but haven't done any perf testing yet.
| } | ||
| #if !defined(NEEDS_INIT_IQ_SHMEM) | ||
| #if !defined(NEEDS_INIT_IQ_SHMEM) && !defined(MULMAT_QUANT) | ||
| barrier(); |
There was a problem hiding this comment.
codex says this barrier shouldn't be skipped for MULMAT_QUANT, since there isn't one in the stub iq_shmem_init implementation
| string_to_spv(shader_name + "_quant_f32" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, quant_float_type_dict), {{"MULMAT_QUANT", "1"}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f32}, {"B_TYPE_SCALAR", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc); | ||
| } | ||
|
|
||
| #if defined(GGML_VULKAN_FLOAT_E2M1_GLSLC_SUPPORT) && defined(GGML_VULKAN_FLOAT_E4M3_GLSLC_SUPPORT) |
There was a problem hiding this comment.
codex claims these are no longer used.
|
|
||
| #ifdef MULMAT_QUANT | ||
|
|
||
| uint mm_quant_k() { |
There was a problem hiding this comment.
Seems like we should have a general function that maps GGML_TYPE to the QUANT_K value.
| uint stride_a = p.stride_a / QUANT_K; | ||
| #ifdef MULMAT_QUANT | ||
| // pos_a is a byte offset into the raw buffer; strides stay in block units | ||
| pos_a *= MmABlockBytes; |
There was a problem hiding this comment.
It might now be more important to make this 64b for the 64bit indexing version of these shaders. But I'm OK with leaving that until somebody complains.
| #endif | ||
| tensorLayoutNV<2, gl_CooperativeMatrixClampModeConstantNV> tensorLayoutD = createTensorLayoutNV(2, gl_CooperativeMatrixClampModeConstantNV); | ||
|
|
||
| #ifndef MULMAT_QUANT |
There was a problem hiding this comment.
I wonder if we could collapse this down and just unconditionally set the block size. But I'm fine with leaving it as-is.
| struct vk_matmul_pipeline_pair { | ||
| vk_pipeline unaligned; | ||
| vk_pipeline aligned; | ||
| uint32_t align; |
There was a problem hiding this comment.
FWIW it's always been unclear to me what the "align" value is supposed to mean for these shaders.
There was a problem hiding this comment.
It is the alignment requirement of the shader, what the K dimension needs to be divisible by to run the shader without bounds checking.
Overview
This is an attempt to reduce the amount of matrix multiplication shaders we need by using a spec constant for the type of the A input, similar to how it works for KV cache types in Flash Attention. Additionally, it replaces the current static s, m and l shapes with a map and allows improved tuning through more shapes and a custom selection function, which should help with work like #24407.
It's still draft because currently it regresses Nvidia, because for dynamic type selection all quants with shared memory requirements must also be available. I thought it would eliminate those that are not used, this works on AMD, but not on Nvidia. @jeffbolznv Do you know why? There are other ways to resolve this, hopefully I'll find something that works.
Requirements