Repository navigation
[GPU] Add UINT2 (2-bit) weight compression support via oneDNN fully_connected - #37212
Conversation
|
The oneDNN submodule is temporarily pointing to mdvoretc/u2 for CI and review. Will update it once the changes make it to oneDNN. |
2ee75c5 to
cb5b4fe
Compare
|
build_jenkins |
717fd16 to
604b51f
Compare
- Add data_types::u2 to compressed_case weight dtype validation - Allow bitwidth==2 in compressed weights assertion - Add u2 to weights padding/strides path for memory descriptor creation - Map cldnn::data_types::u2 to dnnl::memory::data_type::u2 in utils - Handle u2 offset calculation (divide by 4) in get_offset() - Add u2 to decompression zero-point dtype validation - Add GPU_DEBUG_TRACE logging for FC implementation selection - Add U2 memory dump support in debug_helper (4 values per byte)"
604b51f to
4527d82
Compare
|
@Lyamin-Roman @v-Golubev : Could you please review this PR (complementary OV support needed for U2 support needed for oneDNN). The oneDNN submodule in this PR is temporarily pointing to mdvoretc/u2 for CI and review. . I will change this eventually once the PR in oneDNN is merged. |
|
build_jenkins |
1 similar comment
|
build_jenkins |
|
@p-durandin Could you pls review this? |
|
build_jenkins |
# Conflicts: # src/core/reference/src/op/concat.cpp # src/plugins/intel_gpu/src/graph/debug_helper.cpp # src/plugins/intel_gpu/src/graph/impls/onednn/utils.cpp # src/plugins/intel_gpu/thirdparty/onednn_gpu
u2 weight-only-quantized FCs require f16 activations on both the CM and oneDNN kernels; skip per-token INT8 dynamic quantization for them so the u2 path is taken. Carried from openvinotoolkit#37212.
u2 weight-only-quantized FCs require f16 activations on both the CM and oneDNN kernels; skip per-token INT8 dynamic quantization for them so the u2 path is taken. Carried from openvinotoolkit#37212.
…onger u2 FC tests
# Conflicts: # src/plugins/intel_gpu/src/graph/impls/onednn/fully_connected_onednn.cpp
|
Merged latest master (resolved the conflict with #38934 by dropping get_grouped_mask()) and updated oneDNN to uxlfoundation/oneDNN#6296 |
| @@ -23,22 +23,6 @@ struct fully_connected_onednn : typed_primitive_onednn_impl<fully_connected> { | |||
| static constexpr int COMMON = 0; | |||
There was a problem hiding this comment.
random spot)
- onednn update requires broad validation. Let's separate that.
- Please attach a ticket and upload performance/accuracy result there.
There was a problem hiding this comment.
Perf results attached here - https://jira.devtools.intel.com/browse/CVS-198829
# Conflicts: # src/plugins/intel_gpu/thirdparty/onednn_gpu
# Conflicts: # src/plugins/intel_gpu/src/graph/impls/onednn/fully_connected_onednn.cpp # src/plugins/intel_gpu/src/graph/impls/onednn/fully_connected_onednn.hpp # src/plugins/intel_gpu/src/graph/impls/onednn/utils.cpp
23ea61f
…kit#39316) ### This PR fixes micro SDPA config selection on integrated GPUs, which picked a decode (second-token) config for every prefill chunk. ### Tickets: - [CVS-197466](https://jira.devtools.intel.com/browse/CVS-197466) This PR is one part of a set of TTFT optimizations for 2-bit LLMs on Panther Lake. The other parts are openvinotoolkit#39317 (L2 normalization into RMS), openvinotoolkit#39320 (int8 activations for u2 FullyConnected) and uxlfoundation/oneDNN#6381 (u2 weights with int8 activations). --- ### Changes Xe2/Xe3 reach `choose_config_xehpc` for head size <= 128. There were two problems: - **Check order:** the `is_integrated` check returned `xehpc_q_h128_2nd_integrated`, a second-token config, before `thin_q` was checked. With an f16 KV cache, every prefill chunk ran a decode-shaped kernel. - **Unknown key count:** a dynamic key count (StatelessKV) arrives as `seq = 0`, so a prefill over thousands of keys was sized as a short sequence. After the fix: - the integrated decode config is used for `thin_q` only; - a multi-token call with an unknown key count on an integrated GPU is sized as a long sequence. With a quantized KV cache this selects `xehpc_q_h128`, the config that a known long sequence already gets. Discrete GPUs, PagedAttention and decode (`thin_q`) take the same paths as before. --- ### Before / after LLM (2-bit weights, f16 KV cache), PTL 4Xe, ~8200-token prompt in 512-token chunks, TTFT median of 3 runs: | | TTFT | | |---|---|---| | before | 28.92 s | | | after | 24.21 s | **1.19x** | Greedy output is identical and decode speed is unchanged. Running a 2-bit model needs u2 FullyConnected support (openvinotoolkit#37212), so this was measured on a branch that has it. This change does not depend on it. --- ### Validation - The existing SDPA unit tests give the same results with and without this PR. - End to end on PTL 4Xe: greedy output identical to before on all prompts. > [!NOTE] > AI assistance was used. All changes were reviewed, built, tested and measured on hardware.
…oolkit#39469) ### Details: - **Issue 1.** In file _src/plugins/intel_gpu/src/graph/impls/onednn/fully_connected_onednn.cpp_ function _get_ifm_dim_idx()_ guessed which axis of the scale/zero-point tensor holds the number of groups. It derived the axis from the rank of the weights, after a heuristic squeeze. The guess is wrong when the scale or zero-point tensor keeps an extra group-size axis of length 1 that the weights don't have. The extra_multiply cases and 3D weights are examples. The code then picked the OC axis and got a bogus ngroups, for example 32 instead of 1. That made group_size = ifm / ngroups meaningless. oneDNN rejected it. - **Fix 1**. _get_ngroups()_ replaces the axis guess. It computes the group count from element counts, scale_total_count / (OC * batch). It uses the IFM and OC positions from the weights layout, which are reliable. create() and load() both use it, for scales and zero points. This fixes the 4 tests. - **Issue 2.** The tests failed with clWaitForEvents ... CL_INVALID_EVENT, later clEnqueueMemFillINTEL ... CL_OUT_OF_RESOURCES. These are only downstream symptoms. dmesg shows a GPU memory fault: Faulted Address, Engine memory CAT error, Engine reset. The engine reset kills the OpenCL queue, so the next command fails. Cause is OOB read in oneDNN. - **Fix 2.** In separate PR [oneDNN-6417](uxlfoundation/oneDNN#6417. This fixes other 23 tests. - **Issue 3.** The new one came from openvinotoolkit#37212. smoke_MatMulCompressedWeights_basic with u2 weights, shape [377,335], f16 activations and constant weights, one per transpose_weights value. The oneDNN rejecting the weights descriptor with bad dimension strides. In the vendored oneDNN, memory_desc_strides_check (the "generalize strides check for subbyte dts" commit) requires every non-unit stride of a sub-byte descriptor to be a whole number of bytes. For u2 that means a multiple of 4 elements. The weights row stride is the innermost weights dimension. Here it is 335 or 377, so oneDNN refuses the descriptor. - **Fix 3.** In _fully_connected_onednn.hpp_, _validate_impl()_ now rejects the oneDNN FC implementation for compressed weights narrower than 8 bits when the innermost weights dimension isn't a whole number of bytes. It then picks another FC implementation, as it already does for unsupported u2 scale grouping. This fixes the 2 tests. ### Tickets: - *[CVS-199121](https://jira.devtools.intel.com/browse/CVS-199121)* ### AI Assistance: - *AI assistance used: yes*
Details:
This PR enables 2-bit unsigned integer (UINT2) compressed weight support for the GPU plugin's oneDNN
fully_connectedimplementationUINT2 packs 4 weight values per byte, enabling extreme weight compression for LLM inference (e.g., Phi/Orca 2-bit quantized models).
oneDNN fully_connected enablement:
data_types::u2to compressed weight dtype validation and allowbitwidth==2in assertionscldnn::data_types::u2→dnnl::memory::data_type::u2in oneDNN utilsget_offset()and weight padding/strides for memory descriptor creationdebug_helper(4 values per byte)Horizontal FC fusion:
Tickets:
AI Assistance: