const ggml_tensor * src1 = dst->src[1];
if (ggml_is_quantized(src0->type)) {
- GGML_ASSERT(ggml_is_contiguous(src0));
- GGML_ASSERT(ggml_is_contiguous(src1));
+ GGML_ASSERT(ggml_is_contiguous_rows(src0));
+ GGML_ASSERT(ggml_is_contiguous_rows(src1));
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
}
template <typename T>
static void concat_cuda(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, int dim, cudaStream_t stream) {
- if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
+ if (dim != 3 && ggml_is_contiguous_to_3(src0) && ggml_is_contiguous_to_3(src1)) {
const T * src0_d = (const T *) src0->data;
const T * src1_d = (const T *) src1->data;
T * dst_d = (T *) dst->data;
- if (dim != 3) {
- for (int64_t i3 = 0; i3 < dst->ne[3]; i3++) {
- concat_cont_cuda(
- src0_d + i3*(src0->nb[3] / sizeof(T)),
- src1_d + i3*(src1->nb[3] / sizeof(T)),
- dst_d + i3*( dst->nb[3] / sizeof(T)),
- ggml_row_size(src0->type, src0->ne[0])/sizeof(T), src0->ne[1], src0->ne[2],
- ggml_row_size(dst->type, dst->ne[0])/sizeof(T), dst->ne[1], dst->ne[2], dim, stream);
- }
- } else {
- const size_t size0 = ggml_nbytes(src0);
- const size_t size1 = ggml_nbytes(src1);
-
- CUDA_CHECK(cudaMemcpyAsync((char *) dst->data, src0->data, size0, cudaMemcpyDeviceToDevice, stream));
- CUDA_CHECK(cudaMemcpyAsync((char *) dst->data + size0, src1->data, size1, cudaMemcpyDeviceToDevice, stream));
+ for (int64_t i3 = 0; i3 < dst->ne[3]; i3++) {
+ concat_cont_cuda(
+ src0_d + i3*(src0->nb[3] / sizeof(T)),
+ src1_d + i3*(src1->nb[3] / sizeof(T)),
+ dst_d + i3*( dst->nb[3] / sizeof(T)),
+ ggml_row_size(src0->type, src0->ne[0])/sizeof(T), src0->ne[1], src0->ne[2],
+ ggml_row_size(dst->type, dst->ne[0])/sizeof(T), dst->ne[1], dst->ne[2], dim, stream);
}
+ } else if (dim == 3 && ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
+ const size_t size0 = ggml_nbytes(src0);
+ const size_t size1 = ggml_nbytes(src1);
+
+ CUDA_CHECK(cudaMemcpyAsync((char *) dst->data, src0->data, size0, cudaMemcpyDeviceToDevice, stream));
+ CUDA_CHECK(cudaMemcpyAsync((char *) dst->data + size0, src1->data, size1, cudaMemcpyDeviceToDevice, stream));
} else {
GGML_ASSERT(!ggml_is_quantized(src0->type));
GGML_ASSERT(dst->type == src0->type);
if (ggml_is_quantized(src0->type)) {
- GGML_ASSERT(ggml_is_contiguous(src0));
- GGML_ASSERT(ggml_is_contiguous(src1));
+ if (dim == 3) {
+ GGML_ASSERT(ggml_is_contiguous(src0));
+ GGML_ASSERT(ggml_is_contiguous(src1));
+ } else {
+ GGML_ASSERT(ggml_is_contiguous_to_3(src0));
+ GGML_ASSERT(ggml_is_contiguous_to_3(src1));
+ }
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
- // if tensors are contiguous and ne[0] is multiple of the block size we can concat both tensors as byte tensors
+ // if first 3 dimensions are contiguous and ne[0] is multiple of the block size we can concat both tensors as byte tensors
concat_cuda<uint8_t>(src0, src1, dst, dim, stream);
} else {
GGML_ASSERT(ggml_blck_size(src0->type) == 1);
{
ggml_type src0_type = op->src[0]->type;
ggml_type src1_type = op->src[1]->type;
+ const int32_t dim = op->op_params[0];
return src0_type == src1_type &&
src0_type == op->type &&
(
(
ggml_is_quantized(src0_type) &&
- ggml_is_contiguous(op->src[0]) &&
- ggml_is_contiguous(op->src[1]) &&
+ (
+ (
+ dim == 3 &&
+ ggml_is_contiguous(op->src[0]) &&
+ ggml_is_contiguous(op->src[1])
+ ) || (
+ dim != 3 &&
+ ggml_is_contiguous_to_3(op->src[0]) &&
+ ggml_is_contiguous_to_3(op->src[1])
+ )
+ ) &&
op->src[0]->ne[0] % ggml_blck_size(src0_type) == 0 &&
op->src[1]->ne[0] % ggml_blck_size(src0_type) == 0
) || (
const std::array<int64_t, 4> ne_a;
const int64_t ne_b_d;
const int dim;
- const int v; // view (1 << 0: non-cont a, 1 << 1: non-cont b)
+ 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))
std::string vars() override {
return VARS_TO_STR5(type, ne_a, ne_b_d, dim, v);
a = ggml_new_tensor(ctx, type, 4, ne.data());
ggml_set_name(a, "a");
+ 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);
+ ggml_set_name(a, "view_of_a");
+ } else if (v & 4) {
+ auto ne = ne_a; ne[2] *= 2; ne[3] *= 4;
+ a = ggml_new_tensor(ctx, type, 4, ne.data());
+ ggml_set_name(a, "a");
+
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);
ggml_set_name(a, "view_of_a");
} else {
b = ggml_new_tensor(ctx, type, 4, ne.data());
ggml_set_name(b, "b");
+ 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);
+ ggml_set_name(b, "view_of_b");
+ } else if (v & 8) {
+ auto ne = ne_b; ne[2] *= 3; ne[3] *= 2;
+ b = ggml_new_tensor(ctx, type, 4, ne.data());
+ ggml_set_name(b, "b");
+
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);
ggml_set_name(b, "view_of_b");
} else {
}
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 }) {
- for (int dim : { 0, 1, 2, 3, }) {
- test_cases.emplace_back(new test_concat(type_a, {128, 12, 13, 14}, dim == 0 ? 256 : 7, dim, 0));
+ for (int v : { 0, 4, 8, 12 }) {
+ for (int dim : { 0, 1, 2, 3, }) {
+ test_cases.emplace_back(new test_concat(type_a, {128, 12, 13, 14}, dim == 0 ? 256 : 7, dim, v));
+ }
}
}