═══════════════════════════════════════════════════════════════════════════════ TEST 1: Pure dispatch overhead — chain of N trivial add ops ═══════════════════════════════════════════════════════════════════════════════
| 51 | // TEST 1: Pure dispatch overhead — chain of N trivial add ops |
| 52 | // ═══════════════════════════════════════════════════════════════════════════════ |
| 53 | static void test_dispatch_overhead(ggml_backend_t cpu, ggml_backend_t metal) { |
| 54 | fprintf(stderr, "\n=== TEST 1: Dispatch overhead (chain of N add ops on [1024,72,72] tensors) ===\n\n"); |
| 55 | fprintf(stderr, " %6s %10s %10s %12s\n", "N_ops", "CPU (ms)", "Metal (ms)", "Overhead/op"); |
| 56 | |
| 57 | int counts[] = {1, 10, 50, 100, 250, 500, 1000, 2500}; |
| 58 | double metal_1op = 0; |
| 59 | |
| 60 | for (int N : counts) { |
| 61 | int max_tensors = N + 20; |
| 62 | int max_graph = N + 20; |
| 63 | |
| 64 | auto build_and_run = [&](ggml_backend_t backend) -> double { |
| 65 | size_t ctx_size = ggml_tensor_overhead() * max_tensors + ggml_graph_overhead_custom(max_graph, false); |
| 66 | ggml_init_params params = {ctx_size, nullptr, true}; |
| 67 | auto* ctx = ggml_init(params); |
| 68 | |
| 69 | auto* a = ggml_new_tensor_3d(ctx, GGML_TYPE_F32, 1024, 72, 72); ggml_set_input(a); |
| 70 | auto* b = ggml_new_tensor_3d(ctx, GGML_TYPE_F32, 1024, 72, 72); ggml_set_input(b); |
| 71 | auto* x = ggml_add(ctx, a, b); |
| 72 | for (int i = 1; i < N; i++) |
| 73 | x = ggml_add(ctx, x, b); |
| 74 | ggml_set_output(x); |
| 75 | |
| 76 | auto* graph = ggml_new_graph_custom(ctx, max_graph, false); |
| 77 | ggml_build_forward_expand(graph, x); |
| 78 | |
| 79 | std::vector<ggml_tensor*> inputs = {a, b}; |
| 80 | double ms = run_graph(backend, ctx, graph, inputs, 1, 3); |
| 81 | ggml_free(ctx); |
| 82 | return ms; |
| 83 | }; |
| 84 | |
| 85 | double cpu_ms = build_and_run(cpu); |
| 86 | double metal_ms = build_and_run(metal); |
| 87 | |
| 88 | if (N == 1) metal_1op = metal_ms; |
| 89 | double overhead_per_op = (N > 1) ? (metal_ms - metal_1op) / (N - 1) : 0; |
| 90 | |
| 91 | fprintf(stderr, " %6d %8.2f ms %8.2f ms %10.3f ms\n", |
| 92 | N, cpu_ms, metal_ms, overhead_per_op); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | // ═══════════════════════════════════════════════════════════════════════════════ |
| 97 | // TEST 2: Single ViT block as ONE graph vs sum of individual ops |