diff options
author | Kawrakow <iwankawrakow@gmail.com> | 2025-03-10 16:16:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-10 16:16:51 +0200 |
commit | 699c9cb7f63dd8431bce91b86e10efb41255f6c1 (patch) | |
tree | 6000fd823e443f80f90ec490b1bbdf6461902924 /ggml/src/ggml-cuda.cu | |
parent | b096a5de7a9bdf516bb20729d5d0a3b2a12cba2f (diff) |
Faster MoE token generation on CUDA (#248)
* This gives us ~20% TG speedup for DeepSeek on CUDA
* Slightly better
* Also do it for plain (not fused) mul_mat_id
* Guard against numerical precision issues for MLA on CUDA
---------
Co-authored-by: Iwan Kawrakow <iwan.kawrakow@gmail.com>
Diffstat (limited to 'ggml/src/ggml-cuda.cu')
-rw-r--r-- | ggml/src/ggml-cuda.cu | 310 |
1 files changed, 278 insertions, 32 deletions
diff --git a/ggml/src/ggml-cuda.cu b/ggml/src/ggml-cuda.cu index 410c6406..f25dd725 100644 --- a/ggml/src/ggml-cuda.cu +++ b/ggml/src/ggml-cuda.cu @@ -1765,6 +1765,93 @@ static void ggml_cuda_mul_mat_vec_p021(ggml_backend_cuda_context & ctx, const gg ggml_mul_mat_p021_f16_f32_cuda(src0_ddq, src1_ddf, dst_ddf, ne00, ne01, ne02, ne12, main_stream); } +/* +static void ggml_cuda_op_gemv_id( + ggml_backend_cuda_context & ctx, + const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src0_ids, ggml_tensor * dst, ggml_cuda_op_mul_mat_t op, + quantize_cuda_t quantize_src1) { + + GGML_ASSERT(src0->ne[3] == 1); + GGML_ASSERT(ggml_is_contiguous(src0)); + GGML_ASSERT(ggml_is_contiguous(src1)); + GGML_ASSERT(ggml_is_contiguous(dst)); + GGML_ASSERT(ggml_nrows(src1) == 1); + GGML_ASSERT(src0_ids->ne[1] == 1); + GGML_ASSERT(src0_ids->ne[0] <= dst->ne[2]); + GGML_ASSERT(dst->ne[1] == 1); + GGML_ASSERT(src0->ne[0] == src1->ne[0]); + + GGML_ASSERT(ggml_backend_buffer_is_cuda(src0->buffer)); + GGML_ASSERT(ggml_backend_buffer_is_cuda(dst->buffer)); + GGML_ASSERT(ggml_backend_buffer_is_cuda(src1->buffer)); + + ggml_backend_cuda_buffer_context * src0_ctx = (ggml_backend_cuda_buffer_context *) src0->buffer->context; + ggml_backend_cuda_buffer_context * src1_ctx = (ggml_backend_cuda_buffer_context *) src1->buffer->context; + ggml_backend_cuda_buffer_context * dst_ctx = (ggml_backend_cuda_buffer_context *) dst->buffer->context; + + int device_id = ctx.device; + GGML_ASSERT(src0_ctx->device == device_id); + GGML_ASSERT(src1_ctx->device == device_id); + GGML_ASSERT(dst_ctx->device == device_id); + + const bool split = ggml_backend_buffer_is_cuda_split(src0->buffer); + GGML_ASSERT(!split); + + const int64_t ne00 = src0->ne[0]; + const int64_t ne01 = src0->ne[1]; + const int64_t ne02 = src0->ne[2]; + + const int64_t ne10 = src1->ne[0]; + const int64_t nrows1 = 1; + + const int64_t ne0 = dst->ne[0]; + const int64_t ne2 = dst->ne[2]; + + const int64_t nb2 = dst->nb[2]; + + // Why? + GGML_ASSERT(src1->type == GGML_TYPE_F32 || (src1->ne[2] == 1 && src1->ne[3] == 1)); + + const size_t src0_rs = ggml_row_size(src0->type, ne00); + const size_t q8_1_ts = sizeof(block_q8_1); + const size_t q8_1_bs = QK8_1; + + const int64_t src1_padded_col_size = GGML_PAD(ne10, MATRIX_ROW_PADDING); + + ggml_cuda_pool_alloc<char> src0_dd_alloc; + ggml_cuda_pool_alloc<float> src1_ddf_alloc; + ggml_cuda_pool_alloc<char> src1_ddq_alloc; + ggml_cuda_pool_alloc<float> dst_dd_alloc; + + char * src0_dd = nullptr; + float * src1_ddf = (float *)src1->data; + char * src1_ddq = nullptr; // q8_1 + float * dst_dd = (float *)dst->data; + + bool quantization_done = false; + + const bool src1_on_device = device_id == src1_ctx->device; + const bool dst_on_device = device_id == dst_ctx->device; + + ggml_cuda_set_device(device_id); + cudaStream_t stream = ctx.stream(device_id, 0); + + src0_dd = (char *) src0->data; + + if (quantize_src1) { + size_t src_1_ddq_size = nrows1*src1_padded_col_size*q8_1_ts/q8_1_bs; + src1_ddq = src1_ddq_alloc.alloc(ctx.pool(device_id), src_1_ddq_size); + quantize_src1(src1_ddf, src1_ddq, ne10, 1, 1, src1_padded_col_size, src0->type, stream); + } + + ggml_cuda_op_mul_mat_vec_q_id(ctx, src0, src1, src0_ids, dst, + (const char *)src0->data, (const float *)src1->data, src1_ddq, (float *)dst->data, + 0, ne01, 1, src1_padded_col_size, stream); + CUDA_CHECK(cudaGetLastError()); + +} +*/ + static void ggml_cuda_mul_mat_vec_nc(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { GGML_ASSERT(!ggml_is_transposed(src0)); GGML_ASSERT(!ggml_is_transposed(src1)); @@ -2090,6 +2177,52 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor * const ggml_tensor * src1 = dst->src[1]; const ggml_tensor * ids = dst->src[2]; + if (src1->ne[1] == 1 && src1->ne[2] == 1 && src1->ne[3] == 1 && + ggml_is_quantized(src0->type) && + ggml_backend_buffer_is_cuda(src0->buffer) && + ggml_backend_buffer_is_cuda(src1->buffer) && + ggml_backend_buffer_is_cuda(dst->buffer) && + !ggml_backend_buffer_is_cuda_split(src0->buffer) && + src1->type == GGML_TYPE_F32) { + int device_id = ctx.device; + ggml_backend_cuda_buffer_context * src0_ctx = (ggml_backend_cuda_buffer_context *) src0->buffer->context; + ggml_backend_cuda_buffer_context * src1_ctx = (ggml_backend_cuda_buffer_context *) src1->buffer->context; + ggml_backend_cuda_buffer_context * dst_ctx = (ggml_backend_cuda_buffer_context *) dst->buffer->context; + if (src0_ctx->device == device_id && + src1_ctx->device == device_id && + dst_ctx->device == device_id) { + GGML_ASSERT(src1->ne[0] % QK8_1 == 0); + // Fast TG path + const int64_t n_ids = ids->ne[0]; + auto stream = ctx.stream(device_id, 0); + + auto local_dst = *dst; + local_dst.ne[2] = n_ids; + local_dst.ne[1] = local_dst.ne[3] = 1; + local_dst.nb[2] = local_dst.nb[1]; + + auto local_src1 = *src1; + local_src1.nb[2] = local_src1.nb[3] = 0; + + const int64_t src1_padded_col_size = GGML_PAD(src1->ne[0], MATRIX_ROW_PADDING); + ggml_cuda_pool_alloc<char> src1_quantized(ctx.pool()); + auto src_1_ddq_size = src1_padded_col_size*sizeof(block_q8_1)/QK8_1; + local_src1.data = src1_quantized.alloc(src_1_ddq_size); + quantize_row_q8_1_cuda((const float *)src1->data, (void *)src1_quantized.get(), src1->ne[0], 1, 1, src1_padded_col_size, + src0->type, stream); + CUDA_CHECK(cudaGetLastError()); + + local_src1.nb[1] = src_1_ddq_size; + + ggml_cuda_op_mul_mat_vec_q_id(ctx, src0, &local_src1, ids, &local_dst, + (const char *)src0->data, nullptr, src1_quantized.get(), (float *)dst->data, + 0, src0->ne[1], 1, src1_padded_col_size, stream); + CUDA_CHECK(cudaGetLastError()); + + return; + } + } + GGML_TENSOR_BINARY_OP_LOCALS GGML_ASSERT(!ggml_backend_buffer_is_cuda_split(src0->buffer) && "mul_mat_id does not support split buffers"); @@ -2232,6 +2365,121 @@ static bool ggml_cuda_up_gate_unary(ggml_backend_cuda_context & ctx, ggml_tensor const ggml_tensor * src1 = dst->src[2]; const ggml_tensor * ids = dst->src[3]; + if (src1->ne[1] == 1 && src1->ne[2] == 1 && src1->ne[3] == 1 && + ggml_is_quantized(src0_1->type) && + ggml_is_quantized(src0_2->type) && + ggml_backend_buffer_is_cuda(src0_1->buffer) && + ggml_backend_buffer_is_cuda(src0_2->buffer) && + ggml_backend_buffer_is_cuda(src1->buffer) && + ggml_backend_buffer_is_cuda(dst->buffer) && + !ggml_backend_buffer_is_cuda_split(src0_1->buffer) && + !ggml_backend_buffer_is_cuda_split(src0_2->buffer) && + src1->type == GGML_TYPE_F32) { + int device_id = ctx.device; + ggml_backend_cuda_buffer_context * src0_1_ctx = (ggml_backend_cuda_buffer_context *) src0_1->buffer->context; + ggml_backend_cuda_buffer_context * src0_2_ctx = (ggml_backend_cuda_buffer_context *) src0_2->buffer->context; + ggml_backend_cuda_buffer_context * src1_ctx = (ggml_backend_cuda_buffer_context *) src1->buffer->context; + ggml_backend_cuda_buffer_context * dst_ctx = (ggml_backend_cuda_buffer_context *) dst->buffer->context; + if (src0_1_ctx->device == device_id && + src0_2_ctx->device == device_id && + src1_ctx->device == device_id && + dst_ctx->device == device_id) { + // Fast TG path + const int64_t n_ids = ids->ne[0]; + auto stream = ctx.stream(device_id, 0); + ggml_cuda_pool_alloc<char> dst_up_contiguous(ctx.pool(), sizeof(float)*dst->ne[0]*n_ids); + ggml_cuda_pool_alloc<char> dst_gate_contiguous(ctx.pool(), sizeof(float)*dst->ne[0]*n_ids); + + auto local_dst = *dst; + local_dst.ne[2] = n_ids; + local_dst.ne[1] = local_dst.ne[3] = 1; + local_dst.nb[1] = local_dst.nb[2] = local_dst.nb[3] = local_dst.ne[0]*sizeof(float); + + auto local_src1 = *src1; + local_src1.nb[2] = local_src1.nb[3] = 0; + + const int64_t src1_padded_col_size = GGML_PAD(src1->ne[0], MATRIX_ROW_PADDING); + ggml_cuda_pool_alloc<char> src1_quantized(ctx.pool()); + if (ggml_is_quantized(src0_1->type) || ggml_is_quantized(src0_2->type)) { + GGML_ASSERT(src1->ne[0] % QK8_1 == 0); + auto src_1_ddq_size = src1_padded_col_size*sizeof(block_q8_1)/QK8_1; + local_src1.data = src1_quantized.alloc(src_1_ddq_size); + // Note: no use is currently made of the quantization type passed into quantize_row_q8_1_cuda. + // If that were to change, we would need to adjust the code to handle src0_1->type != src0_2->type + quantize_row_q8_1_cuda((const float *)src1->data, (void *)src1_quantized.get(), src1->ne[0], 1, 1, src1_padded_col_size, + src0_1->type, stream); + CUDA_CHECK(cudaGetLastError()); + + local_src1.nb[1] = src_1_ddq_size; + } + + local_dst.data = dst_up_contiguous.get(); + ggml_cuda_op_mul_mat_vec_q_id(ctx, src0_1, &local_src1, ids, &local_dst, + (const char *)src0_1->data, (const float *)src1->data, src1_quantized.get(), (float *)dst_up_contiguous.get(), + 0, src0_1->ne[1], 1, src1_padded_col_size, stream); + CUDA_CHECK(cudaGetLastError()); + + local_dst.data = dst_gate_contiguous.get(); + ggml_cuda_op_mul_mat_vec_q_id(ctx, src0_2, &local_src1, ids, &local_dst, + (const char *)src0_2->data, (const float *)src1->data, src1_quantized.get(), (float *)dst_gate_contiguous.get(), + 0, src0_2->ne[1], 1, src1_padded_col_size, stream); + CUDA_CHECK(cudaGetLastError()); + + if (next && next->op == GGML_OP_MUL_MAT_ID && ggml_is_quantized(next->src[0]->type) && + ggml_backend_buffer_is_cuda(next->src[0]->buffer) && + !ggml_backend_buffer_is_cuda_split(next->src[0]->buffer) && + ((ggml_backend_cuda_buffer_context *)next->src[0]->buffer->context)->device == device_id && + ggml_backend_buffer_is_cuda(next->buffer) && + ((ggml_backend_cuda_buffer_context *)next->buffer->context)->device == device_id) { + + ggml_fused_mul_unary(ctx, (ggml_unary_op)dst->op_params[0], dst->ne[0]*n_ids, + (const float *)dst_gate_contiguous.get(), (const float *)dst_up_contiguous.get(), (float *)dst_gate_contiguous.get()); + CUDA_CHECK(cudaGetLastError()); + + const int64_t dst_padded_col_size = GGML_PAD(dst->ne[0], MATRIX_ROW_PADDING); + GGML_ASSERT(dst->ne[0] % QK8_1 == 0); + auto dst_row_size = dst_padded_col_size*sizeof(block_q8_1)/QK8_1; + auto dst_ddq_size = n_ids*dst_row_size; + ggml_cuda_pool_alloc<char> dst_quantized(ctx.pool(), dst_ddq_size); + quantize_row_q8_1_cuda((const float *)dst_gate_contiguous.get(), (void *)dst_quantized.get(), dst->ne[0], n_ids, 1, + dst_padded_col_size, next->src[0]->type, stream); + CUDA_CHECK(cudaGetLastError()); + + std::vector<char> ids_host(ggml_nbytes(ids)); + const char * ids_dev = (const char *) ids->data; + CUDA_CHECK(cudaMemcpyAsync(ids_host.data(), ids_dev, ggml_nbytes(ids), cudaMemcpyDeviceToHost, stream)); + CUDA_CHECK(cudaStreamSynchronize(stream)); + + local_dst.ne[2] = 1; + + auto local_next = *next; + local_next.ne[2] = local_next.ne[1]; + local_next.ne[1] = local_next.ne[3] = 1; + local_next.nb[2] = local_next.nb[1]; + + local_src1 = *next->src[1]; + local_src1.ne[1] = local_src1.ne[2] = local_src1.ne[3] = 1; + local_src1.nb[1] = local_src1.nb[2] = local_src1.nb[3] = dst_row_size; + + auto local_src0 = *next->src[0]; + local_src0.ne[2] = local_src0.ne[3] = 1; + + ggml_cuda_op_mul_mat_vec_q_id(ctx, &local_src0, &local_src1, ids, &local_next, + (const char *)next->src[0]->data, nullptr, dst_quantized.get(), (float *)next->data, + 0, next->src[0]->ne[1], 1, dst_padded_col_size, stream); + CUDA_CHECK(cudaGetLastError()); + + return true; + } else { + ggml_fused_mul_unary(ctx, (ggml_unary_op)dst->op_params[0], ggml_nelements(dst), + (const float *)dst_gate_contiguous.get(), (const float *)dst_up_contiguous.get(), (float *)dst->data); + CUDA_CHECK(cudaGetLastError()); + return false; + } + } + } + + GGML_TENSOR_BINARY_OP_LOCALS GGML_ASSERT(!ggml_backend_buffer_is_cuda_split(src0_1->buffer) && "mul_mat_id does not support split buffers"); @@ -2299,49 +2547,47 @@ static bool ggml_cuda_up_gate_unary(ggml_backend_cuda_context & ctx, ggml_tensor if (fuse_down) { final_dst.src[1] = &dst_row; } - for (int64_t iid1 = 0; iid1 < ids->ne[1]; iid1++) { - for (int64_t id = 0; id < n_ids; id++) { - const int32_t i02 = *(const int32_t *) (ids_host.data() + iid1*ids->nb[1] + id*ids->nb[0]); + for (int64_t id = 0; id < n_ids; id++) { + const int32_t i02 = *(const int32_t *) (ids_host.data() + id*ids->nb[0]); - if (i02 < 0 || i02 >= n_as) continue; - //GGML_ASSERT(i02 >= 0 && i02 < n_as); + if (i02 < 0 || i02 >= n_as) continue; + //GGML_ASSERT(i02 >= 0 && i02 < n_as); - const int64_t i11 = id % ne11; - const int64_t i12 = iid1; + const int64_t i11 = id % ne11; + const int64_t i12 = 0; - const int64_t i1 = id; - const int64_t i2 = i12; + const int64_t i1 = id; + const int64_t i2 = i12; - src0_1_row.data = src0_1_original + i02*nb02; - src0_2_row.data = src0_2_original + i02*nb02; - src1_row.data = src1_original + i11*nb11 + i12*nb12; - //dst_row.data = dst_original + i1*nb1 + i2*nb2; + src0_1_row.data = src0_1_original + i02*nb02; + src0_2_row.data = src0_2_original + i02*nb02; + src1_row.data = src1_original + i11*nb11 + i12*nb12; + //dst_row.data = dst_original + i1*nb1 + i2*nb2; - dst_row.data = dst_up_contiguous.get(); - ggml_cuda_mul_mat(ctx, &src0_1_row, &src1_row, &dst_row); - CUDA_CHECK(cudaGetLastError()); + dst_row.data = dst_up_contiguous.get(); + ggml_cuda_mul_mat(ctx, &src0_1_row, &src1_row, &dst_row); + CUDA_CHECK(cudaGetLastError()); - dst_row.data = dst_gate_contiguous.get(); - ggml_cuda_mul_mat(ctx, &src0_2_row, &src1_row, &dst_row); - CUDA_CHECK(cudaGetLastError()); + dst_row.data = dst_gate_contiguous.get(); + ggml_cuda_mul_mat(ctx, &src0_2_row, &src1_row, &dst_row); + CUDA_CHECK(cudaGetLastError()); - if (fuse_down) { - ggml_fused_mul_unary(ctx, (ggml_unary_op)dst->op_params[0], dst_row.ne[0], - (const float *)dst_gate_contiguous.get(), (const float *)dst_up_contiguous.get(), (float *)dst_gate_contiguous.get()); - CUDA_CHECK(cudaGetLastError()); + if (fuse_down) { + ggml_fused_mul_unary(ctx, (ggml_unary_op)dst->op_params[0], dst_row.ne[0], + (const float *)dst_gate_contiguous.get(), (const float *)dst_up_contiguous.get(), (float *)dst_gate_contiguous.get()); + CUDA_CHECK(cudaGetLastError()); - final_src.data = (char *)next->src[0]->data + i02*next->src[0]->nb[2]; - final_dst.data = (char *)next->data + i1*next->nb[1] + i2*next->nb[2]; - ggml_cuda_mul_mat(ctx, &final_src, &dst_row, &final_dst); - CUDA_CHECK(cudaGetLastError()); + final_src.data = (char *)next->src[0]->data + i02*next->src[0]->nb[2]; + final_dst.data = (char *)next->data + i1*next->nb[1] + i2*next->nb[2]; + ggml_cuda_mul_mat(ctx, &final_src, &dst_row, &final_dst); + CUDA_CHECK(cudaGetLastError()); - } else { + } else { - ggml_fused_mul_unary(ctx, (ggml_unary_op)dst->op_params[0], dst_row.ne[0], - (const float *)dst_gate_contiguous.get(), (const float *)dst_up_contiguous.get(), (float *)(dst_original + i1*nb1 + i2*nb2)); - CUDA_CHECK(cudaGetLastError()); + ggml_fused_mul_unary(ctx, (ggml_unary_op)dst->op_params[0], dst_row.ne[0], + (const float *)dst_gate_contiguous.get(), (const float *)dst_up_contiguous.get(), (float *)(dst_original + i1*nb1 + i2*nb2)); + CUDA_CHECK(cudaGetLastError()); - } } } } else { |