Skip to content

Commit c569a07

Browse files
PMZFXclaude
authored andcommitted
[SYCL] Fix Q8_0 reorder: garbage on 2nd prompt + crash on full VRAM (ggml-org#21638)
* [SYCL] Fix Q8_0 reorder: add missing dequantize path for GEMM The Q8_0 reorder optimization (ggml-org#21527) was missing a reorder-aware dequantizer for the GEMM code path used during prompt processing. After token generation reordered Q8_0 weights (via DMMV/MMVQ), the next prompt processing pass would read them with the standard dequantizer, producing garbage output. Add dequantize_block_q8_0_reorder() and wire it into both ggml_get_to_fp16_sycl() and ggml_get_to_fp32_sycl(), matching the pattern already used by Q4_0, Q4_K, and Q6_K. Fixes ggml-org#21589 AI (Claude) was used to assist with root cause investigation and writing the kernel code. All code was human-reviewed and tested on real hardware. * SYCL: fix reorder crash when device memory is full The reorder optimization allocates a temporary buffer the full size of the weight tensor on the device. When VRAM is nearly full (large models on a single GPU), this allocation fails and the subsequent memcpy crashes on a NULL pointer. Fix: try device allocation first, fall back to host memory if device memory is full. The reorder kernel still works correctly reading from host memory over PCIe. This is slower for the one-time reorder (~21 t/s vs ~38 t/s on Intel Arc Pro B70), but the optimization is preserved for all subsequent inference. If both device and host allocation fail, skip the reorder and fall back to the unoptimized kernel path. Also fixes a bug where opt_for_reorder() marked tensors as reordered even when the reorder was skipped due to allocation failure. This caused DMMV/MMVQ kernels to read the original AoS data as if it were SoA, producing garbage output or NaN results. Tested on Intel Arc Pro B70 (32GB) with Q8_0, Q4_K_M models. Coding was AI-assisted (Claude), reviewed and tested on hardware by a human. Fixes ggml-org#20478 * SYCL: add RAII temp buffer class + macro guard for host fallback Replace sycl_ext_malloc_with_fallback/sycl_ext_free_fallback free functions with sycl_reorder_temp_buffer RAII class. The host_fallback bool is now a private member, and cleanup happens automatically at scope exit. Add GGML_SYCL_HOST_MEM_FALLBACK cmake option (default ON) to guard the host memory fallback code path. Device access to host memory requires Linux kernel 6.8+ (Ubuntu 26.04+); users on older kernels can set -DGGML_SYCL_HOST_MEM_FALLBACK=OFF to disable it. Addresses arthw's review on PR ggml-org#21638. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * SYCL: document GGML_SYCL_HOST_MEM_FALLBACK build option in SYCL.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * SYCL: add reorder-aware DMMV dequantizers for Q4_K and Q6_K Q4_K and Q6_K had reorder support for MMVQ and GEMM paths but not DMMV. When the DMMV path encountered reordered data it would abort. Add DMMV kernels that read from the SOA reorder layout for both types. Same math as the non-reorder versions, different memory access pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a237ead commit c569a07

7 files changed

Lines changed: 466 additions & 29 deletions

File tree

docs/backend/SYCL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ use 1 SYCL GPUs: [0] with Max compute units:512
689689
| GGML_SYCL_F16 | OFF *(default)* \|ON *(optional)* | Enable FP16 build with SYCL code path. (1.) |
690690
| GGML_SYCL_GRAPH | OFF *(default)* \|ON *(Optional)* | Enable build with [SYCL Graph extension](https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/experimental/sycl_ext_oneapi_graph.asciidoc). |
691691
| GGML_SYCL_DNN | ON *(default)* \|OFF *(Optional)* | Enable build with oneDNN. |
692+
| GGML_SYCL_HOST_MEM_FALLBACK | ON *(default)* \|OFF *(Optional)* | Allow host memory fallback when device memory is full during quantized weight reorder. Enables inference to continue at reduced speed (reading over PCIe) instead of failing. Requires Linux kernel 6.8+. |
692693
| CMAKE_C_COMPILER | `icx` *(Linux)*, `icx/cl` *(Windows)* | Set `icx` compiler for SYCL code path. |
693694
| CMAKE_CXX_COMPILER | `icpx` *(Linux)*, `icx` *(Windows)* | Set `icpx/icx` compiler for SYCL code path. |
694695

ggml/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ option(GGML_RPC "ggml: use RPC"
254254
option(GGML_SYCL "ggml: use SYCL" OFF)
255255
option(GGML_SYCL_F16 "ggml: use 16 bit floats for sycl calculations" OFF)
256256
option(GGML_SYCL_GRAPH "ggml: enable graphs in the SYCL backend" ON)
257+
option(GGML_SYCL_HOST_MEM_FALLBACK "ggml: allow host memory fallback in SYCL reorder (requires kernel 6.8+)" ON)
257258
option(GGML_SYCL_DNN "ggml: enable oneDNN in the SYCL backend" ON)
258259
set (GGML_SYCL_TARGET "INTEL" CACHE STRING
259260
"ggml: sycl target device")

ggml/src/ggml-sycl/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ if (GGML_SYCL_GRAPH)
154154
target_compile_definitions(ggml-sycl PRIVATE GGML_SYCL_GRAPH)
155155
endif()
156156

157+
if (GGML_SYCL_HOST_MEM_FALLBACK)
158+
message(STATUS "find GGML_SYCL_HOST_MEM_FALLBACK")
159+
target_compile_definitions(ggml-sycl PRIVATE GGML_SYCL_HOST_MEM_FALLBACK)
160+
endif()
161+
157162
if (GGML_SYCL_DEVICE_ARCH)
158163
target_compile_options(ggml-sycl PRIVATE -Xsycl-target-backend --offload-arch=${GGML_SYCL_DEVICE_ARCH})
159164
target_link_options(ggml-sycl PRIVATE -Xsycl-target-backend --offload-arch=${GGML_SYCL_DEVICE_ARCH})

ggml/src/ggml-sycl/convert.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,25 @@ static void dequantize_row_q4_0_sycl_reorder(const void *vx, dst_t *y, const int
151151

152152
}
153153

154+
template <typename dst_t>
155+
static void dequantize_row_q8_0_sycl_reorder(const void *vx, dst_t *y, const int64_t k,
156+
dpct::queue_ptr stream) {
157+
158+
dpct::has_capability_or_fail(stream->get_device(),
159+
{sycl::aspect::fp16});
160+
161+
int constexpr WARP_K = WARP_SIZE * QK8_0;
162+
const int n_warp = (k + WARP_K - 1) / WARP_K;
163+
GGML_ASSERT(k % QK8_0 == 0);
164+
stream->parallel_for(sycl::nd_range<3>(sycl::range<3>(1, 1, n_warp) *
165+
sycl::range<3>(1, 1, WARP_SIZE),
166+
sycl::range<3>(1, 1, WARP_SIZE)),
167+
[=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]]{
168+
dequantize_block_q8_0_reorder(vx, y, k, item_ct1);
169+
});
170+
171+
}
172+
154173
template <typename dst_t>
155174
static void dequantize_row_q4_1_sycl(const void *vx, dst_t *y, const int64_t k,
156175
dpct::queue_ptr stream) {
@@ -614,7 +633,12 @@ to_fp16_sycl_t ggml_get_to_fp16_sycl(ggml_type type, ggml_tensor * dst) {
614633
case GGML_TYPE_Q5_1:
615634
return dequantize_block_sycl<QK5_1, QR5_1, dequantize_q5_1>;
616635
case GGML_TYPE_Q8_0:
617-
return dequantize_block_sycl<QK8_0, QR8_0, dequantize_q8_0>;
636+
if (dst->src[0]->extra &&
637+
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
638+
return dequantize_row_q8_0_sycl_reorder;
639+
} else {
640+
return dequantize_block_sycl<QK8_0, QR8_0, dequantize_q8_0>;
641+
}
618642
case GGML_TYPE_Q2_K:
619643
return dequantize_row_q2_K_sycl;
620644
case GGML_TYPE_Q3_K:
@@ -683,7 +707,12 @@ to_fp32_sycl_t ggml_get_to_fp32_sycl(ggml_type type, ggml_tensor *dst) {
683707
case GGML_TYPE_Q5_1:
684708
return dequantize_block_sycl<QK5_1, QR5_1, dequantize_q5_1>;
685709
case GGML_TYPE_Q8_0:
686-
return dequantize_block_sycl<QK8_0, QR8_0, dequantize_q8_0>;
710+
if (dst->src[0]->extra &&
711+
((ggml_tensor_extra_gpu*)dst->src[0]->extra)->optimized_feature.reorder) {
712+
return dequantize_row_q8_0_sycl_reorder;
713+
} else {
714+
return dequantize_block_sycl<QK8_0, QR8_0, dequantize_q8_0>;
715+
}
687716
case GGML_TYPE_Q2_K:
688717
return dequantize_row_q2_K_sycl;
689718
case GGML_TYPE_Q3_K:

ggml/src/ggml-sycl/dequantize.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,34 @@ static void dequantize_block_q4_0_reorder(const void * __restrict__ vx, dst_t *
239239

240240
}
241241

242+
// Dequantize Q8_0 from reorder layout: [all qs (k bytes)][all d values]
243+
// Each thread handles one block of QK8_0 elements.
244+
template<typename dst_t>
245+
static void dequantize_block_q8_0_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t k,
246+
const sycl::nd_item<3> &item_ct1) {
247+
248+
const int64_t i = item_ct1.get_group(2);
249+
const int64_t tid = item_ct1.get_local_id(2);
250+
const int lane_ib = i * WARP_SIZE + tid;
251+
252+
if (lane_ib >= k / QK8_0) {
253+
return;
254+
}
255+
256+
dst_t * y_ptr = yy + lane_ib * QK8_0;
257+
258+
auto qs = (const int8_t*)vx + lane_ib * QK8_0;
259+
auto s_ptr = (const sycl::half*)((const uint8_t*)vx + k) + lane_ib;
260+
261+
const float d = float(*s_ptr);
262+
263+
#pragma unroll
264+
for (int l = 0; l < QK8_0; ++l) {
265+
y_ptr[l] = d * qs[l];
266+
}
267+
268+
}
269+
242270
template<typename dst_t>
243271
static void dequantize_block_q4_1(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t nb32,
244272
const sycl::nd_item<3> &item_ct1) {

0 commit comments

Comments
 (0)