Skip to content

Commit a4ce259

Browse files
authored
opencl: avoid the vec path in GEMV for unaligned row stride (ggml-org#25671)
The f16 GEMV kernels take a vectorized path for ne00 >= 128 that casts the row pointers to half4 or float4. When the row stride is not aligned, the wide load becomes misaligned. On devices that require natural alignment for vector loads, the kernel reads garbage. This is the case Intel GPUs and the kernels produce incorrect results there. Adreno happpens to be byte addressable and the kernels happen to work.
1 parent c718542 commit a4ce259

3 files changed

Lines changed: 24 additions & 3 deletions

File tree

ggml/src/ggml-opencl/kernels/mul_mv_f16_f16.cl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,14 @@ kernel void kernel_mul_mat_f16_f16(
6464

6565
global half * x = (global half *) (src0 + offset_src0);
6666

67-
if (ne00 < 128) {
67+
// The vector path below casts the row pointers to half4, which must be 8-byte aligned.
68+
// A row address is r0*nb01 + ..., and a permuted or strided src leaves nb01/nb11
69+
// unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned. Every
70+
// src1 row this work-item walks is src1_base + r1*nb11, so require both.
71+
const ulong src1_base = (ulong) (src1 + (i12)*nb12 + (i13)*nb13);
72+
const bool row_aligned = (((ulong) x) & 7) == 0 && (src1_base & 7) == 0 && (nb11 & 7) == 0;
73+
74+
if (ne00 < 128 || !row_aligned) {
6875
for (int row = 0; row < N_F16_F16; ++row) {
6976
int r1 = rb + row;
7077
if (r1 >= ne11) {

ggml/src/ggml-opencl/kernels/mul_mv_f16_f32.cl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,14 @@ kernel void kernel_mul_mat_f16_f32(
6464

6565
global half * x = (global half *) (src0 + offset_src0);
6666

67-
if (ne00 < 128) {
67+
// The vector path below casts the row pointers to half4/float4, which must be 8- and
68+
// 16-byte aligned. A row address is r0*nb01 + ..., and a permuted or strided src leaves
69+
// nb01/nb11 unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned.
70+
// Every src1 row this work-item walks is src1_base + r1*nb11, so require both.
71+
const ulong src1_base = (ulong) (src1 + (i12)*nb12 + (i13)*nb13);
72+
const bool row_aligned = (((ulong) x) & 7) == 0 && (src1_base & 15) == 0 && (nb11 & 15) == 0;
73+
74+
if (ne00 < 128 || !row_aligned) {
6875
for (int row = 0; row < N_F16_F32; ++row) {
6976
int r1 = rb + row;
7077
if (r1 >= ne11) {

ggml/src/ggml-opencl/kernels/mul_mv_f16_f32_1row.cl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,15 @@ kernel void kernel_mul_mat_f16_f32_1row(
6464
global half * x = (global half *) (src0 + offset_src0);
6565
global float * y = (global float *) (src1 + offset_src1);
6666

67+
// The vector path below casts the row pointers to half4/float4, which must be 8- and
68+
// 16-byte aligned. A row address is r0*nb01 + ..., and a permuted or strided src leaves
69+
// nb01/nb11 unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned.
70+
// Take the vector path only when the rows this work-item touches are actually aligned;
71+
// the scalar loop has no such requirement.
72+
const bool row_aligned = (((ulong) x) & 7) == 0 && (((ulong) y) & 15) == 0;
73+
6774
float sumf = 0;
68-
if (ne00 < 128) {
75+
if (ne00 < 128 || !row_aligned) {
6976
for (int i = get_sub_group_local_id(); i < ne00; i += get_max_sub_group_size()) {
7077
sumf += (float) x[i] * (float) y[i];
7178
}

0 commit comments

Comments
 (0)