Repository navigation
[GPU] Support u2 weights in the optimized bf_tiled FC kernel - #39058
Cyberpunk1210 wants to merge 11 commits into
Conversation
PR openvinotoolkit#36170 enabled u2 on FullyConnected_bfyx_Ref, the one-work-item-per-output reference kernel. This enables it on bf_tiled, the optimized OCL path, so a u2 FC on a device that does not take the oneDNN path (no XMX, or oneDNN disabled) is not left on the reference kernel. u2 is not dynamic-quantizable, so it takes the non-dyn-quant tilings, with constraints that all come from 4 values sharing one byte: - Validate: IFM must be a multiple of 4, and swiglu fusion is excluded (it needs os_is_yx_osv32_isv2, which cannot be produced for u2). - VerifyTuneParams: only TILE_IFM 2 / TILE_K 4, whose per-lane weight staging reads exactly one byte, matching both u2 packings; SLM only when IFM divides evenly so no partial byte is left over. - Jit: tile_k_ofm_packed /= 4; FILTER_ELEMENTS_PER_BYTE is a jit constant, not a .cl #define, because kernels are concatenated into one batched program and only jit constants get an automatic #undef. - Layouts: os_iyx_osv16 and os_is_yx_osv64_isv2. Supporting changes: - reorder_weights_int4 gains u2 variants of its three layout paths (OIYX padding, IOYX->OIYX transpose, os_iyx_osv16 repack), gathering 4 values per output byte and zero-filling the padded tail; fetch_weights.cl gets the matching osv16 index for 4-values-per-byte packing. - prepare_padding aligns u2 weights to 4 values per byte instead of 2; fake alignment and the async-compilation rule treat u2 like u4. - kernel_selector toCLType maps u2 to uchar, like u4. - DynamicQuantizeFullyConnected declines u2: no kernel implements i8 activations x u2 weights. openvinotoolkit#37212 adds the same rule in the pipeline callback; whichever lands second should drop one of them. Tests: bf_tiled u2 FC over batch / IFM / OFM sizes and scale group sizes, with and without a grouped zero point, plus an os_is_yx_osv64_isv2 case; u2 reorder_data cases (identity, u2->f16, f16->u2 clamping and partial bytes). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com> # Conflicts: # src/plugins/intel_gpu/tests/unit/test_cases/fully_connected_gpu_test.cpp
Merges current master (via the updated openvinotoolkit#39058/openvinotoolkit#39059 branches) so the combined branch stays mergeable; content is still exactly the seven split PRs. Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
build_jenkins |
Comment-only cleanup in the u2 bf_tiled / reorder_weights_int4 changes and their tests (keep the why, drop restated code and history), plus the toCLType case order requested in the GatherCompressed review. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
build_jenkins |
Return the conditions directly (clang-tidy readability-simplify-boolean-expr). No behaviour change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
build_jenkins |
| // #undef'd between kernels of a batched program. | ||
| jit.AddConstant(MakeJitConstant("FILTER_ELEMENTS_PER_BYTE", weights_dt == WeightsType::UINT2 ? 4 : 2)); | ||
| if (weights_dt == WeightsType::UINT2) { | ||
| jit.AddConstant(MakeJitConstant("COMPRESSED_WEIGHTS_INT4", 1)); |
There was a problem hiding this comment.
nit: probably better not mix this name COMPRESSED_WEIGHTS_INT4 into u2 path
There was a problem hiding this comment.
Done: bf_tiled now uses its own COMPRESSED_WEIGHTS_SUB_BYTE (i4/u4/u2) for the shared packed-weight paths, and u2 no longer sets COMPRESSED_WEIGHTS_INT4.
u2 no longer sets COMPRESSED_WEIGHTS_INT4; bf_tiled defines its own macro for i4/u4/u2 and its kernels switch to it. Rename only. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
build_jenkins |
| const uint h = output_index / OUTPUT_INNERMOST_NUM; | ||
| if (w < INPUT0_INNERMOST_NUM) { | ||
| const uint in_elem_offset = h * INPUT0_INNERMOST_NUM + w; | ||
| out |= ((input[in_elem_offset / 4] >> ((in_elem_offset % 4) * 2)) & 0x3) << (j * 2); |
There was a problem hiding this comment.
nit: please split out | expression to more vars
There was a problem hiding this comment.
Done: split into in_shift / val locals.
…order expression Move the "no FC kernel takes i8 activations with u2 weights" check out of the DynamicQuantizeFullyConnected pass body into its pass_config callback, so the pass itself matches upstream and DynamicQuantizeFullyConnectedTests (u2 case) passes; plugin behaviour is unchanged. Also split the packed-u2 reorder expression in reorder_weights_int4.cl into named locals. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
…ts_int4 Use in_shift/val locals instead of the compound bit expression in the IOYX->OIYX branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
build_jenkins |
| auto weights_layout_dt = orig_impl_param.weights_layout.value().data_type; | ||
| auto is_4bit = weights_layout_dt == data_types::i4 || weights_layout_dt == data_types::u4; | ||
| auto is_4bit = weights_layout_dt == data_types::i4 || weights_layout_dt == data_types::u4 || | ||
| weights_layout_dt == data_types::u2; |
There was a problem hiding this comment.
nit: please ad is_2bit
There was a problem hiding this comment.
Done: added is_2bit for u2; is_4bit is back to i4/u4.
| #elif FILTER_LAYOUT_OS_IS_YX_OSV64_ISV2 | ||
| SLM_FILTER_PACKED_VEC wei_packed0 = BLOCK_READN(FILTER_TYPE, FILTER_ACTUAL_LOAD_BLOCK_SIZE, weights, weights_idx); | ||
| #if COMPRESSED_WEIGHTS_UINT2 | ||
| SLM_FILTER_PACKED_VEC wei_packed1 = BLOCK_READN(FILTER_TYPE, FILTER_ACTUAL_LOAD_BLOCK_SIZE, weights, (weights_idx + (FILTER_LOAD_BLOCK_SIZE * SIMD / 2))); |
There was a problem hiding this comment.
please change this line to: SLM_FILTER_PACKED_VEC wei_packed1 = BLOCK_READN(FILTER_TYPE, FILTER_ACTUAL_LOAD_BLOCK_SIZE, weights, (weights_idx + U2_OSV64_ISV2_LINE_SIZE_BYTES)); where U2_OSV64_ISV2_LINE_SIZE_BYTES is 32
There was a problem hiding this comment.
Done: the read now uses U2_OSV64_ISV2_LINE_SIZE_BYTES (32), defined at the top of the kernel.
|
please add SLM test |
…ests Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
Added u2 bf_tiled SLM tests: |
prepare_padding.cpp conflicted with openvinotoolkit#39073; take upstream's version. The u2 change there only touched the oneDNN branch, and oneDNN FC does not take u2 weights, so it was never reached. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
build_jenkins |
| #endif | ||
|
|
||
| #if COMPRESSED_WEIGHTS_INT4 | ||
| #if COMPRESSED_WEIGHTS_SUB_BYTE |
There was a problem hiding this comment.
looks like this branch is not touched by u2 - please express that in #if
There was a problem hiding this comment.
Done, added && COMPRESSED_WEIGHTS_INT4.
| @@ -55,7 +86,23 @@ KERNEL(reorder_weights_int4)(const __global INPUT0_TYPE* input, __global OUTPUT_ | |||
|
|
|||
There was a problem hiding this comment.
[random spot][nit] please change filename to sth like ....compressed... to not contain int4 explicitly
There was a problem hiding this comment.
Done, renamed to reorder_weights_compressed.
… the osv32 path int4-only The kernel now also repacks u2, so the int4 name is misleading. The osv32_isv2 branch of the bf_tiled FC kernel is never taken for u2; say so in the #if. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
|
||
| if (root->get_input_element_type(1) == ov::element::u2) { | ||
| GPU_DEBUG_TRACE << root->get_friendly_name() << " dyn_quan is turned off:" | ||
| " no FC kernel takes i8 activations with u2 weights" << std::endl; |
There was a problem hiding this comment.
please change comment to be more broad - not contain any compressed activation not only i8
There was a problem hiding this comment.
Done, reworded to "compressed activations".
|
build_jenkins |
|
overall LGTM |
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Cyberpunk1210 <genji.1231@outlook.com>
|
build_jenkins |
v-Golubev
left a comment
There was a problem hiding this comment.
A question on the overall direction. oneDNN already supports u2 FC (#37212): it uses plain weights layouts, so no repacking to os_iyx_osv16 / os_is_yx_osv64_isv2 and no extra reorder kernels. This path looks preferable, since it means much less code to maintain on the OpenVINO side and it avoids extra memory consumption.
Could you please clarify which code paths this PR closes that oneDNN wouldn't? Do we have real workloads (models/devices) that hit them?
Details:
Part 2 of the split of #37335 (series: #39057 … #39063). No dependencies. The batch-1 GEMV
kernel follows in #39059.
#36170 enabled u2 FC on the reference kernel
fully_connected_gpu_bfyx_ref; #37212 adds u2 FC viaoneDNN for XMX platforms. This PR enables u2 on
fully_connected_gpu_bf_tiled, so a u2 FC that doesnot take the oneDNN path is not left on the reference kernel. That covers two cases: devices
without XMX (or with oneDNN disabled), and u2 models with a fractional zero point. oneDNN's
weight zero point must be an integer type (i4/u4/u8/i8), but the symmetric 4-level grid
{-1.5, -0.5, +0.5, +1.5}·scale used by some 2-bit models is
(q - 1.5)·scale, i.e. a scalar zp of1.5. #37212 already declines such a layer at implementation selection (its zero-point dtype check
admits i4/u4/u8/i8 only), so the layer falls to the OCL kernels; with this PR that is bf_tiled, which
takes the zero point in accumulator precision and decodes the grid exactly, instead of the reference
kernel. (#37212's selection code notes that "u2 has no OCL fallback"; after this PR it has one.)
Relation to #38460 (TernOCL):
ternocl_int2takes u2 FCs with a scalar zero point of exactly 1(ternary {-1, 0, +1}) on Xe2+ with DPAS, and is selected ahead of the other implementations. Every
other u2 FC — e.g. NNCF INT2_SYM (scalar zp 2), grouped zero points, fractional zero points, or
devices without DPAS — is not taken by it and lands here (and on the GEMV kernel of #39059 at
batch 1).
TILE_K 4 (one weight byte per lane),
os_iyx_osv16andos_is_yx_osv64_isv2layouts.reorder_weights_int4: u2 OIYX padding, IOYX->OIYX transpose andos_iyx_osv16repack;matching osv16 index in
fetch_weights.cl.prepare_paddingaligns u2 weights to 4 values per byte; fake alignment and theasync-compilation rule treat u2 like u4.
toCLTypemaps u2 touchar(same two lines as [GPU] Support u2 compressed dictionaries in GatherCompressed #39057).DynamicQuantizeFullyConnecteddeclines u2. [GPU] Add UINT2 (2-bit) weight compression support via oneDNN fully_connected #37212 adds the same rule in the pipelinecallback; whichever lands second should drop one of them.
Tests: bf_tiled u2 FC over batch / IFM / OFM sizes and scale group sizes, with and without a grouped
zero point, plus an
os_is_yx_osv64_isv2case; u2reorder_datacases.Tickets:
AI Assistance:
close supervision.
*fully_connected*:*reorder*unit tests (1575 passed, 32 skipped, 0 failed).🤖 Generated with Claude Code