Skip to content

Commit d164904

Browse files
metal : optimize Metal Tensor API usage for GGML_OP_MUL_MAT (ggml-org#20962)
* Optimize Metal Tensor API usage for matmul2d Separates the Metal Tensor API (matmul2d) path in kernel_mul_mm into its own standalone kernel, gated by GGML_METAL_HAS_TENSOR. The legacy simdgroup_matrix kernel is preserved under #else. Previously both paths were interleaved via #ifdef blocks within a single kernel, forcing the tensor path to share the legacy kernel's data layout and threadgroup memory scheme. Splitting the kernel enabled memory and dispatch optimizations that weren't possible when the two paths shared code structure. * cont : cleanup * cont : cleanup * cont : cleanup --------- Co-authored-by: Georgi Gerganov <ggerganov@gmail.com>
1 parent 9d34231 commit d164904

6 files changed

Lines changed: 189 additions & 109 deletions

File tree

ggml/src/ggml-metal/ggml-metal-device.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,15 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mm(ggml_meta
677677
const ggml_type tsrc1 = op->src[1]->type;
678678

679679
const bool bc_inp = op->src[0]->ne[0] % 32 != 0;
680-
const bool bc_out = op->ne[0] % 64 != 0 || op->ne[1] % 32 != 0;
680+
681+
constexpr int NRA = SZ_SIMDGROUP * N_MM_BLOCK_Y * N_MM_SIMD_GROUP_Y;
682+
constexpr int NRB = SZ_SIMDGROUP * N_MM_BLOCK_X * N_MM_SIMD_GROUP_X;
683+
684+
const bool has_tensor = ggml_metal_device_get_props(ggml_metal_library_get_device(lib))->has_tensor;
685+
686+
const bool bc_out = has_tensor
687+
? (op->ne[0] % NRA != 0 || op->ne[1] % NRB != 0)
688+
: (op->ne[0] % 64 != 0 || op->ne[1] % 32 != 0);
681689

682690
snprintf(base, 256, "kernel_mul_mm_%s_%s", ggml_type_name(tsrc0), ggml_type_name(tsrc1));
683691
snprintf(name, 256, "%s_bci=%d_bco=%d", base, bc_inp, bc_out);
@@ -694,8 +702,20 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mm(ggml_meta
694702
ggml_metal_cv_free(cv);
695703
}
696704

697-
// when the output size is not multiple of 64x32, we need extra smem to prevent out-of-bounds writes
698-
res.smem = bc_out ? 8192 : 4096 + 2048;
705+
if (has_tensor) {
706+
res.nr0 = NRA;
707+
res.nr1 = NRB;
708+
709+
const size_t smem_a = NRA * N_MM_NK_TOTAL * sizeof(ggml_fp16_t);
710+
res.smem = smem_a;
711+
} else {
712+
res.nr0 = 64;
713+
res.nr1 = 32;
714+
715+
res.smem = bc_out ? 8192 : (4096 + 2048);
716+
}
717+
718+
res.nsg = N_MM_SIMD_GROUP_X * N_MM_SIMD_GROUP_Y;
699719

700720
return res;
701721
}

ggml/src/ggml-metal/ggml-metal-device.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ ggml_metal_library_t ggml_metal_library_init_from_source(ggml_metal_device_t dev
102102

103103
void ggml_metal_library_free(ggml_metal_library_t lib);
104104

105+
ggml_metal_device_t ggml_metal_library_get_device(ggml_metal_library_t lib);
106+
105107
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline (ggml_metal_library_t lib, const char * name);
106108
struct ggml_metal_pipeline_with_params ggml_metal_library_compile_pipeline(ggml_metal_library_t lib, const char * base, const char * name, ggml_metal_cv_t cv);
107109

ggml/src/ggml-metal/ggml-metal-device.m

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ int ggml_metal_pipeline_max_theads_per_threadgroup(struct ggml_metal_pipeline_wi
9595

9696
struct ggml_metal_library {
9797
id<MTLLibrary> obj;
98-
id<MTLDevice> device;
9998

99+
ggml_metal_device_t dev;
100100
ggml_metal_pipelines_t pipelines; // cache of compiled pipelines
101101

102102
NSLock * lock;
@@ -251,7 +251,7 @@ ggml_metal_library_t ggml_metal_library_init(ggml_metal_device_t dev) {
251251
ggml_metal_library_t res = calloc(1, sizeof(struct ggml_metal_library));
252252

253253
res->obj = library;
254-
res->device = device;
254+
res->dev = dev;
255255
res->pipelines = ggml_metal_pipelines_init();
256256
res->lock = [NSLock new];
257257

@@ -318,7 +318,7 @@ ggml_metal_library_t ggml_metal_library_init_from_source(ggml_metal_device_t dev
318318
}
319319

320320
res->obj = library;
321-
res->device = device;
321+
res->dev = dev;
322322
res->pipelines = ggml_metal_pipelines_init();
323323
res->lock = [NSLock new];
324324

@@ -341,6 +341,10 @@ void ggml_metal_library_free(ggml_metal_library_t lib) {
341341
free(lib);
342342
}
343343

344+
ggml_metal_device_t ggml_metal_library_get_device(ggml_metal_library_t lib) {
345+
return lib->dev;
346+
}
347+
344348
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline(ggml_metal_library_t lib, const char * name) {
345349
[lib->lock lock];
346350

@@ -405,7 +409,8 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_compile_pipeline(ggml_
405409
return res;
406410
}
407411

408-
id<MTLComputePipelineState> obj = [lib->device newComputePipelineStateWithFunction:mtl_function error:&error];
412+
id<MTLDevice> device = ggml_metal_device_get_obj(lib->dev);
413+
id<MTLComputePipelineState> obj = [device newComputePipelineStateWithFunction:mtl_function error:&error];
409414

410415
[mtl_function release];
411416

@@ -699,7 +704,7 @@ ggml_metal_device_t ggml_metal_device_init(int device) {
699704
" auto sB = tB.slice(0, 0); \n"
700705
" mm.run(sB, sA, cT); \n"
701706
" \n"
702-
" auto tC = tensor<device float, dextents<int32_t, 2>, tensor_inline>(C, dextents<int32_t, 2>(4, 4)); \n"
707+
" auto tC = tensor<device float, dextents<int32_t, 2>, tensor_inline>(C, dextents<int32_t, 2>(16, 16)); \n"
703708
" \n"
704709
" cT.store(tC); \n"
705710
"}";
@@ -749,7 +754,7 @@ ggml_metal_device_t ggml_metal_device_init(int device) {
749754
" auto sB = tB.slice(0, 0); \n"
750755
" mm.run(sB, sA, cT); \n"
751756
" \n"
752-
" auto tC = tensor<device float, dextents<int32_t, 2>, tensor_inline>(C, dextents<int32_t, 2>(4, 4)); \n"
757+
" auto tC = tensor<device float, dextents<int32_t, 2>, tensor_inline>(C, dextents<int32_t, 2>(16, 16)); \n"
753758
" \n"
754759
" cT.store(tC); \n"
755760
"}";

ggml/src/ggml-metal/ggml-metal-impl.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
#ifndef GGML_METAL_IMPL
22
#define GGML_METAL_IMPL
33

4+
// kernel parameters for mat-mat threadgroups
5+
//
6+
// TODO: become function constants
7+
8+
#define SZ_SIMDGROUP 16
9+
#define N_MM_NK 2
10+
#define N_MM_NK_TOTAL (SZ_SIMDGROUP * N_MM_NK)
11+
12+
#define N_MM_BLOCK_X 4
13+
#define N_MM_BLOCK_Y 2
14+
#define N_MM_SIMD_GROUP_X 2
15+
#define N_MM_SIMD_GROUP_Y 2
16+
417
// kernel parameters for mat-vec threadgroups
518
//
619
// N_R0: number of src0 rows to process per simdgroup

ggml/src/ggml-metal/ggml-metal-ops.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2195,7 +2195,12 @@ int ggml_metal_op_mul_mat(ggml_metal_op_t ctx, int idx) {
21952195
const size_t smem = pipeline.smem;
21962196

21972197
ggml_metal_encoder_set_threadgroup_memory_size(enc, smem, 0);
2198-
ggml_metal_encoder_dispatch_threadgroups(enc, ((ne11 + 31)/32), ((ne01 + 63)/64), ne12*ne13, 128, 1, 1);
2198+
2199+
const int nr0 = pipeline.nr0;
2200+
const int nr1 = pipeline.nr1;
2201+
const int nsg = pipeline.nsg;
2202+
2203+
ggml_metal_encoder_dispatch_threadgroups(enc, ((ne11 + nr1 - 1) / nr1), ((ne01 + nr0 - 1) / nr0), ne12 * ne13, 32, nsg, 1);
21992204
} else {
22002205
auto pipeline = ggml_metal_library_get_pipeline_mul_mv(lib, op);
22012206

0 commit comments

Comments
 (0)