Skip to content

[GPU] Refactor for FC Compressed - #38697

Merged
Kotomi-Du merged 16 commits into
openvinotoolkit:masterfrom
Kotomi-Du:refactor_fccompressed
Oct 14, 2026
Merged

Kotomi-Du merged 16 commits into
openvinotoolkit:masterfrom
Kotomi-Du:refactor_fccompressed

Conversation

@Kotomi-Du

@Kotomi-Du Kotomi-Du commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Details:

The reason for this refactor is to make the code cleaner and better prepare for upcoming features:

Changes:

  1. Use the new pattern matching operators (| and optional) to simplify the patterns.
  2. Add transpose_b_scale and transpose_b_zp to track the actual layout of scale/zp.

Current logic:

  1. If weights and scale/zp are transposed, scale/zp will be reordered back to a non-transposed layout to satisfy kernel requirements.

  2. If weights and scale/zp are not transposed, weights and scale/zp will first be reordered to a transposed layout. Since the kernel still requires scale/zp in a non-transposed layout, another reorder is added to convert scale/zp back.

As a result, in case 2, scale/zp are unnecessarily transposed and then transposed back. This increases memory usage and adds extra compilation work. The impact is currently limited because the tensors are constants and the cost is paid only during compilation.

With this PR:
By tracking the actual layout of scale/zp, the plugin can skip the unnecessary transpose-and-revert steps in case 2. This simplifies the logic, reduces memory usage, and will be especially helpful when weights/scale/zp are provided as parameters instead of constants.

Data flow (non-transposed case)
Weights [K,N] --Transpose--> FC (transpose_b=true)
Scale   [G,N] -------------> FC (transpose_b_scale=false)   // no Transpose, no reorder
ZP      [G,N] -------------> FC (transpose_b_zp=false)
   OCL : bfyx[G,N] reinterpreted as fbyx (N=batch, G=feature)
   oneDNN: ngroups read from axis 0 ([G,N])

Tickets:

AI Assistance:

AI assistance used to implement fix and unit test. The generated code was verified for correctness, manually built, executed, and fixed as needed.

fully_connected_3d_onednn_gpu.compressed_int4_scale_static:fusings_gpu/permute_activation_scale_eltwise.basic/15:fusings_gpu/permute_activation_scale_eltwise.basic/16:fusings_gpu/permute_activation_scale_eltwise.basic/17:fusings_gpu/permute_activation_scale_eltwise.basic/18:fusings_gpu/permute_activation_scale_eltwise.basic/19:fusings_gpu/permute_activation_scale_eltwise.basic/20:fusings_gpu/permute_activation_scale_eltwise.basic/21:fusings_gpu/permute_activation_scale_eltwise.basic/40:fusings_gpu/permute_activation_scale_eltwise.basic/41:fusings_gpu/permute_activation_scale_eltwise.basic/42:fusings_gpu/permute_activation_scale_eltwise.basic/43:fusings_gpu/permute_activation_scale_eltwise.basic/44:fusings_gpu/permute_act_reorder.basic/6:fusings_gpu/permute_act_reorder.basic/7:fusings_gpu/permute_act_reorder.basic/8:fusings_gpu/fc_compressed_dyn_quan_and_quantized.basic/0
@Kotomi-Du
Kotomi-Du requested review from a team as code owners October 9, 2026 00:52
@Kotomi-Du
Kotomi-Du requested a balanced review from Copilot and removed request for a team October 9, 2026 00:53
@github-actions github-actions Bot added the category: GPU OpenVINO GPU plugin label Oct 9, 2026
@Kotomi-Du

Copy link
Copy Markdown
Contributor Author

@CuriousPanCake Please help review 06ecc68 related to transformation change.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Non-transposed per-channel parameters are misclassified in oneDNN, and horizontal fusion concatenates them along the wrong axis.

4 open findings
What changed in this PR

Refactors GPU compressed fully connected handling to track decompression scale/ZP orientation and avoid redundant transposes.

Changes:

  • Simplifies compressed-weight matching with optional/alternative patterns.
  • Propagates and serializes scale/ZP transpose attributes.
  • Updates OCL/oneDNN layout handling and tests.
File Description
src/​plugins/​intel_gpu/​tests/​unit/​transformations/​convert_fc_to_compressed_test.cpp Updates transformation references for retained parameter layouts.
src/​plugins/​intel_gpu/​tests/​unit/​test_cases/​fully_connected_gpu_test.cpp Adds non-transposed scale/ZP inference coverage.
src/​plugins/​intel_gpu/​src/​plugin/​transformations/​op/​fully_connected_compressed.cpp Implements attributes, cloning, and serialization visits.
src/​plugins/​intel_gpu/​src/​plugin/​transformations/​fc_horizontal_fusion.cpp Propagates layout attributes through horizontal fusion.
src/​plugins/​intel_gpu/​src/​plugin/​transformations/​fc_convert_fusion.cpp Preserves attributes during convert fusion.
src/​plugins/​intel_gpu/​src/​plugin/​transformations/​dynamic_quantize_fully_connected.cpp Preserves attributes during dynamic quantization.
src/​plugins/​intel_gpu/​src/​plugin/​transformations/​convert_matmul_to_fc.cpp Adopts the refactored compressed-weight pattern.
src/​plugins/​intel_gpu/​src/​plugin/​transformations/​convert_fc_to_compressed.cpp Tracks actual scale/ZP orientation without extra transposes.
src/​plugins/​intel_gpu/​src/​plugin/​transformations/​compressed_weights_pattern.hpp Simplifies and extends compressed-weight matching.
src/​plugins/​intel_gpu/​src/​plugin/​ops/​fully_connected.cpp Passes orientation attributes into the primitive.
src/​plugins/​intel_gpu/​src/​graph/​impls/​onednn/​fully_connected_onednn.cpp Uses orientation when configuring grouped decompression.
src/​plugins/​intel_gpu/​src/​graph/​impls/​ocl/​fully_connected.cpp Reinterprets non-transposed decompression tensors.
src/​plugins/​intel_gpu/​src/​graph/​graph_optimizer/​prepare_quantization.cpp Skips unnecessary parameter reorders.
src/​plugins/​intel_gpu/​include/​intel_gpu/​primitives/​fully_connected.hpp Adds primitive attributes and cache serialization.
src/​plugins/​intel_gpu/​include/​intel_gpu/​op/​fully_connected_compressed.hpp Exposes scale/ZP orientation attributes.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Kotomi-Du
Kotomi-Du requested a review from p-durandin October 9, 2026 01:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The horizontal fusion implementation contains two type mismatches that prevent compilation.

1 open finding
4 resolved since last review

🧠 Review effort: Balanced

… types for OutputVector elements'

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The refactored pattern can crash on reshaped scales, and failed ZP fusion dereferences a null result.

1 open finding
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Matched scale reshape is discarded during compressed FC conversion

src/​plugins/​intel_gpu/​src/​plugin/​transformations/​compressed_weights_pattern.hpp:43

This optional scale reshape is matched but then discarded: the callback still builds fc_input_scale from the raw mul_const_m. For a valid 3D decompression such as weights [G,N,K] with scale [G,N] -> Reshape([G,N,1]), the callback sees a rank-2 scale and reaches the constant-weight assertion at convert_fc_to_compressed.cpp:147 instead of leaving or converting the graph. Consume/fold the matched mul_const_reshape_m when constructing the compressed FC, or do not match this form yet.

🧠 Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Orientation derivation and broadened patterns can mis-handle valid compressed-weight layouts and change decompression results.

2 open findings
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Handle padded weight ranks when selecting the N axis

src/​plugins/​intel_gpu/​src/​plugin/​transformations/​fc_horizontal_fusion.cpp:155

For non-transposed parameters, rank - 1 is not always the N axis: legacy padded constants can be [groups,N,1,1] (the same representation handled in prepare_quantization.cpp:578-589). This selects axis 3, so FCs with different N sizes fail Concat validation instead of being fused along axis 1. Determine the logical two-dimensional suffix/prefix after accounting for trailing unit padding and leading broadcast dimensions, as the oneDNN helper does.

🧠 Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-backend tensor-layout and cache-serialization changes warrant final GPU maintainer review.

0 open findings

2 resolved since last review

🧠 Review effort: Balanced

@Kotomi-Du
Kotomi-Du force-pushed the refactor_fccompressed branch from a9d439e to 8eb7f80 Compare October 10, 2026 03:10

@e-ddykim e-ddykim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check my issue reproducers here --> e-ddykim@c9c3275

@Kotomi-Du
Kotomi-Du requested a review from a team as a code owner October 14, 2026 06:04
@github-actions github-actions Bot added category: build OpenVINO cmake script / infra category: transformations OpenVINO Runtime library - Transformations labels Oct 14, 2026
@Kotomi-Du

Copy link
Copy Markdown
Contributor Author

Please check my issue reproducers here --> e-ddykim@c9c3275

fixed and added related unit tests

@e-ddykim e-ddykim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@github-actions github-actions Bot removed the category: build OpenVINO cmake script / infra label Oct 14, 2026
@Kotomi-Du
Kotomi-Du added this pull request to the merge queue Oct 14, 2026
Merged via the queue into openvinotoolkit:master with commit 5b555b1 Oct 14, 2026
260 of 266 checks passed
@Kotomi-Du
Kotomi-Du deleted the refactor_fccompressed branch October 14, 2026 20:48
sivanov-work pushed a commit to sivanov-work/openvino that referenced this pull request Oct 22, 2026
### Details:
- openvinotoolkit#38697 added handling for an OC-broadcast decompression
scale/zero-point (`[1, G]` / `[G, 1]`) in the oneDNN FullyConnected
implementation: `get_grouped_mask()` clears the OC bit from the grouped
scale/zp mask whenever the OC dim of the scale/zp is 1.
- That check didn't consider whether the weights' N is also 1, in which
case the scale isn't really broadcast. For example, u4 weights `[1,
2048]` with a transposed scale `[1, 16]` (group size 128) lost the OC
bit; for a 2D FC that leaves mask = 1, K only. oneDNN's gemm doesn't
support that mask, so oneDNN falls back to the reference `ocl:ref:any`
matmul, a performance regression for this dynamic-shape FC.
- On re-evaluation, the OC-broadcast handling is overly defensive. The
standard weight-compression flow (NNCF/optimum) always produces per-OC
scale/zp `[N, G, 1]`, which
`ConvertFullyConnectedToFullyConnectedCompressed` reshapes to `[N, G]`,
so the scale/zp OC dim always matches the weights' N.
- This PR removes `get_grouped_mask()` and restores the full `grouped`
mask for grouped scale/zp in both `create()` and `load()`, i.e. the
behavior before openvinotoolkit#38697. It also removes the
`compressed_int8_broadcast_scale_zp` unit tests, which covered only the
removed path.

### Tickets:
 - *[CVS-197314](https://jira.devtools.intel.com/browse/CVS-197314)*

### AI Assistance:
 - AI assistance used: yes
- Used AI to trace oneDNN matmul implementation selection (`gemm_t` vs
`ref_t`), analyze the scale/zp mask computed by `get_grouped_mask()`,
check where scale/zp shapes come from in the compression pipeline, and
draft this description.
wgzintel pushed a commit to wgzintel/openvino that referenced this pull request Nov 5, 2026
…e treated as grouped case (openvinotoolkit#39268)

## Description 
For weights [64, N, K] and zero points [64, N, 1], the old code
incorrectly read N as the group count but accidentally selected a
grouped mask that preserved per-expert zero points. After the PR openvinotoolkit#38697
correctly identified one group along K, the existing rank check selected
a per-channel mask that ignored the expert axis, causing all experts to
reuse the first expert’s zero points. The fix explicitly checks whether
weights have a batch/expert dimension and retains the grouped mask for
3D weights, including during cache loading.

| Stage | IFM-group axis read | `ngroups` | Zero-point mask | Expert
handling |
|---|---|---|---|---|
| Before PR openvinotoolkit#38697 | 1 (incorrect) | N (incorrect) | 7, grouped |
Preserved by accident |
| After PR openvinotoolkit#38697, before fix | 2 (correct) | 1 (correct) | 4,
per-channel | Expert axis ignored |
| After current fix | 2 (correct) | 1 (correct) | 7, grouped | Expert
axis preserved |

## Details
**Affected topology:**
- Weights: `[64, N, K]`, with `transpose_b = true`.
- Zero points: `[64, N, 1]`, with `transpose_b_zp = true`.
- Assume `N > 1` and `K > 1`, so the weights' non-unit dimension count
is 3.

#### Before the PR openvinotoolkit#38697: correct expert handling by accident

The old code chose the zero-point IFM axis using the number of non-unit
zero-point dimensions:

```cpp
// Zero-point shape: [64, N, 1]
dzp_rank = count_if(dims > 1);             // 2, because the trailing 1 is ignored
dzp_ifm_dim_idx = weights_transposed
                     ? dzp_rank - 1
                     : dzp_rank - 2;     // 1
ngroups = dzp_layout.get_dim(1);          // N, not the actual group count of 1
```

Because `ngroups != 1`, the code selected the grouped branch:

```cpp
attr->set_zero_points(DNNL_ARG_WEIGHTS, grouped, {group_size, 1}, dzp_data_type);
```

For a 3D FC, `grouped = (1 << 3) - 1 = 7`. This mask includes the expert
dimension, so different experts retain their own zero points. The
observed case worked, but the axis selection and inferred group count
were wrong.

#### After the PR openvinotoolkit#38697, before the fix: correct axis exposes the
mask-selection bug

The new orientation-aware helper correctly identifies the IFM-group
axis:

```cpp
get_ifm_dim_idx(dzp_layout, true, 3);  // 2
ngroups = dzp_layout.get_dim(2);      // 1
```

However, the per-channel condition still used the non-unit zero-point
rank:

```cpp
// dzp_rank is still 2 for [64, N, 1].
if (ngroups == 1 && dzp_rank <= 2) {
    attr->set_zero_points(DNNL_ARG_WEIGHTS, per_oc, {}, dzp_data_type);
}
```

It didn't land to the condition which has group attribute.

#### After the fix in current PR
Mask selection now explicitly checks whether the weights have a
batch/expert dimension:

```cpp
const bool has_batch_dim = weight_rank > 2;
if (ngroups == 1 && !has_batch_dim) {
    attr->set_zero_points(DNNL_ARG_WEIGHTS, per_oc, {}, dzp_data_type);
} else {
    attr->set_zero_points(DNNL_ARG_WEIGHTS, grouped, {group_size, 1}, dzp_data_type);
}
```

For `[64, N, K]`, `has_batch_dim` is `true`. Even with `ngroups == 1`,
the code keeps the grouped mask 7 and preserves per-expert zero points.
Ordinary 2D per-channel weights still use the per-channel branch.

The same batch-dimension check is applied to scale and zero-point
attributes during cache loading, keeping grouped handling consistent
with fresh compilation.

## Ticket
* CVS-198171
## AI Assistance:
* Yes
* Used AI to find the root cause; engineer manually build OV- GenAI to
validate the fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category: GPU OpenVINO GPU plugin category: transformations OpenVINO Runtime library - Transformations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants