Skip to content

Commit a3e5b96

Browse files
cuda : relax tensor contiguity requirements for quantized concat (ggml-org#25678)
* cuda : relax tensor contiguity requirements for quantized concat * tests : add test cases for non-contiguous quantized concat * ggml : relax contiguity requirements for quantized concat --------- Co-authored-by: Stanisław Szymczyk <sszymczy@gmail.com>
1 parent c810293 commit a3e5b96

4 files changed

Lines changed: 55 additions & 26 deletions

File tree

ggml/src/ggml-cpu/ops.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,8 +2081,8 @@ void ggml_compute_forward_concat(
20812081
const ggml_tensor * src1 = dst->src[1];
20822082

20832083
if (ggml_is_quantized(src0->type)) {
2084-
GGML_ASSERT(ggml_is_contiguous(src0));
2085-
GGML_ASSERT(ggml_is_contiguous(src1));
2084+
GGML_ASSERT(ggml_is_contiguous_rows(src0));
2085+
GGML_ASSERT(ggml_is_contiguous_rows(src1));
20862086
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
20872087
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
20882088
}

ggml/src/ggml-cuda/concat.cu

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -141,27 +141,25 @@ static __global__ void __launch_bounds__(CUDA_CONCAT_BLOCK_SIZE)
141141

142142
template <typename T>
143143
static void concat_cuda(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, int dim, cudaStream_t stream) {
144-
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
144+
if (dim != 3 && ggml_is_contiguous_to_3(src0) && ggml_is_contiguous_to_3(src1)) {
145145
const T * src0_d = (const T *) src0->data;
146146
const T * src1_d = (const T *) src1->data;
147147
T * dst_d = (T *) dst->data;
148148

149-
if (dim != 3) {
150-
for (int64_t i3 = 0; i3 < dst->ne[3]; i3++) {
151-
concat_cont_cuda(
152-
src0_d + i3*(src0->nb[3] / sizeof(T)),
153-
src1_d + i3*(src1->nb[3] / sizeof(T)),
154-
dst_d + i3*( dst->nb[3] / sizeof(T)),
155-
ggml_row_size(src0->type, src0->ne[0])/sizeof(T), src0->ne[1], src0->ne[2],
156-
ggml_row_size(dst->type, dst->ne[0])/sizeof(T), dst->ne[1], dst->ne[2], dim, stream);
157-
}
158-
} else {
159-
const size_t size0 = ggml_nbytes(src0);
160-
const size_t size1 = ggml_nbytes(src1);
161-
162-
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data, src0->data, size0, cudaMemcpyDeviceToDevice, stream));
163-
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data + size0, src1->data, size1, cudaMemcpyDeviceToDevice, stream));
149+
for (int64_t i3 = 0; i3 < dst->ne[3]; i3++) {
150+
concat_cont_cuda(
151+
src0_d + i3*(src0->nb[3] / sizeof(T)),
152+
src1_d + i3*(src1->nb[3] / sizeof(T)),
153+
dst_d + i3*( dst->nb[3] / sizeof(T)),
154+
ggml_row_size(src0->type, src0->ne[0])/sizeof(T), src0->ne[1], src0->ne[2],
155+
ggml_row_size(dst->type, dst->ne[0])/sizeof(T), dst->ne[1], dst->ne[2], dim, stream);
164156
}
157+
} else if (dim == 3 && ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
158+
const size_t size0 = ggml_nbytes(src0);
159+
const size_t size1 = ggml_nbytes(src1);
160+
161+
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data, src0->data, size0, cudaMemcpyDeviceToDevice, stream));
162+
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data + size0, src1->data, size1, cudaMemcpyDeviceToDevice, stream));
165163
} else {
166164
GGML_ASSERT(!ggml_is_quantized(src0->type));
167165

@@ -208,12 +206,17 @@ void ggml_cuda_op_concat(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
208206
GGML_ASSERT(dst->type == src0->type);
209207

210208
if (ggml_is_quantized(src0->type)) {
211-
GGML_ASSERT(ggml_is_contiguous(src0));
212-
GGML_ASSERT(ggml_is_contiguous(src1));
209+
if (dim == 3) {
210+
GGML_ASSERT(ggml_is_contiguous(src0));
211+
GGML_ASSERT(ggml_is_contiguous(src1));
212+
} else {
213+
GGML_ASSERT(ggml_is_contiguous_to_3(src0));
214+
GGML_ASSERT(ggml_is_contiguous_to_3(src1));
215+
}
213216
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
214217
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
215218

216-
// if tensors are contiguous and ne[0] is multiple of the block size we can concat both tensors as byte tensors
219+
// if first 3 dimensions are contiguous and ne[0] is multiple of the block size we can concat both tensors as byte tensors
217220
concat_cuda<uint8_t>(src0, src1, dst, dim, stream);
218221
} else {
219222
GGML_ASSERT(ggml_blck_size(src0->type) == 1);

ggml/src/ggml-cuda/ggml-cuda.cu

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4816,13 +4816,23 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
48164816
{
48174817
ggml_type src0_type = op->src[0]->type;
48184818
ggml_type src1_type = op->src[1]->type;
4819+
const int32_t dim = op->op_params[0];
48194820
return src0_type == src1_type &&
48204821
src0_type == op->type &&
48214822
(
48224823
(
48234824
ggml_is_quantized(src0_type) &&
4824-
ggml_is_contiguous(op->src[0]) &&
4825-
ggml_is_contiguous(op->src[1]) &&
4825+
(
4826+
(
4827+
dim == 3 &&
4828+
ggml_is_contiguous(op->src[0]) &&
4829+
ggml_is_contiguous(op->src[1])
4830+
) || (
4831+
dim != 3 &&
4832+
ggml_is_contiguous_to_3(op->src[0]) &&
4833+
ggml_is_contiguous_to_3(op->src[1])
4834+
)
4835+
) &&
48264836
op->src[0]->ne[0] % ggml_blck_size(src0_type) == 0 &&
48274837
op->src[1]->ne[0] % ggml_blck_size(src0_type) == 0
48284838
) || (

tests/test-backend-ops.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5555,7 +5555,7 @@ struct test_concat : public test_case {
55555555
const std::array<int64_t, 4> ne_a;
55565556
const int64_t ne_b_d;
55575557
const int dim;
5558-
const int v; // view (1 << 0: non-cont a, 1 << 1: non-cont b)
5558+
const int v; // view (1 << 0: non-cont a (first 3 dim), 1 << 1: non-cont b (first 3 dim), 1 << 2: non-cont a (last 2 dim), 1 << 3: non-cont b (last 2 dim))
55595559

55605560
std::string vars() override {
55615561
return VARS_TO_STR5(type, ne_a, ne_b_d, dim, v);
@@ -5576,6 +5576,13 @@ struct test_concat : public test_case {
55765576
a = ggml_new_tensor(ctx, type, 4, ne.data());
55775577
ggml_set_name(a, "a");
55785578

5579+
a = ggml_view_4d(ctx, a, ne_a[0], ne_a[1], ne_a[2], ne_a[3], a->nb[1], a->nb[2], a->nb[3], 0);
5580+
ggml_set_name(a, "view_of_a");
5581+
} else if (v & 4) {
5582+
auto ne = ne_a; ne[2] *= 2; ne[3] *= 4;
5583+
a = ggml_new_tensor(ctx, type, 4, ne.data());
5584+
ggml_set_name(a, "a");
5585+
55795586
a = ggml_view_4d(ctx, a, ne_a[0], ne_a[1], ne_a[2], ne_a[3], a->nb[1], a->nb[2], a->nb[3], 0);
55805587
ggml_set_name(a, "view_of_a");
55815588
} else {
@@ -5588,6 +5595,13 @@ struct test_concat : public test_case {
55885595
b = ggml_new_tensor(ctx, type, 4, ne.data());
55895596
ggml_set_name(b, "b");
55905597

5598+
b = ggml_view_4d(ctx, b, ne_b[0], ne_b[1], ne_b[2], ne_b[3], b->nb[1], b->nb[2], b->nb[3], 0);
5599+
ggml_set_name(b, "view_of_b");
5600+
} else if (v & 8) {
5601+
auto ne = ne_b; ne[2] *= 3; ne[3] *= 2;
5602+
b = ggml_new_tensor(ctx, type, 4, ne.data());
5603+
ggml_set_name(b, "b");
5604+
55915605
b = ggml_view_4d(ctx, b, ne_b[0], ne_b[1], ne_b[2], ne_b[3], b->nb[1], b->nb[2], b->nb[3], 0);
55925606
ggml_set_name(b, "view_of_b");
55935607
} else {
@@ -9089,8 +9103,10 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
90899103
}
90909104

90919105
for (ggml_type type_a : { GGML_TYPE_Q4_0, GGML_TYPE_Q4_1, GGML_TYPE_Q5_0, GGML_TYPE_Q5_1, GGML_TYPE_Q8_0 }) {
9092-
for (int dim : { 0, 1, 2, 3, }) {
9093-
test_cases.emplace_back(new test_concat(type_a, {128, 12, 13, 14}, dim == 0 ? 256 : 7, dim, 0));
9106+
for (int v : { 0, 4, 8, 12 }) {
9107+
for (int dim : { 0, 1, 2, 3, }) {
9108+
test_cases.emplace_back(new test_concat(type_a, {128, 12, 13, 14}, dim == 0 ? 256 : 7, dim, v));
9109+
}
90949110
}
90959111
}
90969112

0 commit comments

Comments
 (0)