|
14 | 14 | #include "ggml-impl.h" |
15 | 15 |
|
16 | 16 | #include <assert.h> |
| 17 | +#include <inttypes.h> |
17 | 18 | #include <limits.h> |
18 | 19 | #include <stdarg.h> |
19 | 20 | #include <stdio.h> |
20 | 21 | #include <stdlib.h> |
21 | 22 | #include <string.h> |
22 | 23 | #include <string> |
23 | 24 | #include <vector> |
| 25 | +#include <set> |
24 | 26 |
|
25 | 27 | #ifdef __APPLE__ |
26 | 28 | #include <sys/types.h> |
@@ -1997,3 +1999,199 @@ ggml_backend_buffer_t ggml_backend_cpu_buffer_from_ptr(void * ptr, size_t size) |
1997 | 1999 | GGML_ASSERT((uintptr_t)ptr % TENSOR_ALIGNMENT == 0 && "buffer pointer must be aligned"); |
1998 | 2000 | return ggml_backend_buffer_init(ggml_backend_cpu_buffer_from_ptr_type(), ggml_backend_cpu_buffer_from_ptr_i, ptr, size); |
1999 | 2001 | } |
| 2002 | + |
| 2003 | +void ggml_backend_sched_splits_fdump_dot(FILE * fp, ggml_backend_sched_t sched, const struct ggml_cgraph * graph) { |
| 2004 | + std::set<void *> visited; |
| 2005 | + |
| 2006 | + for (int i = 0; i < sched->n_splits; i++) { |
| 2007 | + struct ggml_backend_sched_split * split = &sched->splits[i]; |
| 2008 | + ggml_backend_t split_backend = sched->backends[split->backend_id]; |
| 2009 | + |
| 2010 | + fprintf(fp, " subgraph cluster_%d {" |
| 2011 | + " node [style=filled];" |
| 2012 | + " label = \"SPLIT %d : %s # %d inputs\";\n", |
| 2013 | + i, i, ggml_backend_name(split_backend), split->n_inputs); |
| 2014 | + |
| 2015 | + for (int j = split->i_start; j < split->i_end; j++) { |
| 2016 | + struct ggml_tensor *node = graph->nodes[j]; |
| 2017 | + fprintf(fp, " \"%p\";\n", node); |
| 2018 | + for (int k = 0; k < GGML_MAX_SRC; k++) { |
| 2019 | + struct ggml_tensor *src = node->src[k]; |
| 2020 | + if ( (nullptr == src) |
| 2021 | + || (tensor_backend_id(src) != split->backend_id) |
| 2022 | + || (visited.find(src) != visited.end())) { |
| 2023 | + continue; |
| 2024 | + } |
| 2025 | + |
| 2026 | + visited.insert(src); |
| 2027 | + fprintf(fp, " \"%p\";\n", src); |
| 2028 | + } |
| 2029 | + } |
| 2030 | + fprintf(fp, " }\n"); |
| 2031 | + } |
| 2032 | +} |
| 2033 | + |
| 2034 | +static uint32_t simple_hash(const unsigned char *str) |
| 2035 | +{ |
| 2036 | + uint32_t hash = 5381; |
| 2037 | + while (unsigned char c = *str++) hash = hash * 33 + c; |
| 2038 | + return hash; |
| 2039 | +} |
| 2040 | + |
| 2041 | +static const char *ggml_color_of_backend(ggml_backend_sched_t sched, struct ggml_tensor * node) { |
| 2042 | +#ifndef GGML_DOT_FULL_COLOR |
| 2043 | + #define COLOR_NUM 12 |
| 2044 | + #define DEF_COLOR 0 |
| 2045 | +#else |
| 2046 | + #define COLOR_NUM 0x1000000 |
| 2047 | + #define DEF_COLOR 0x0ffffff |
| 2048 | +#endif |
| 2049 | + |
| 2050 | + static char color[32]; |
| 2051 | + uint32_t color1 = DEF_COLOR; |
| 2052 | + uint32_t color2 = DEF_COLOR; |
| 2053 | + |
| 2054 | + ggml_backend_t backend = ggml_backend_sched_get_tensor_backend(sched, node); |
| 2055 | + if (backend) { |
| 2056 | + const char *name = ggml_backend_name(backend); |
| 2057 | + color1 = simple_hash((const unsigned char *)name) % COLOR_NUM; |
| 2058 | + color2 = color1; |
| 2059 | + } |
| 2060 | + if (node->buffer) { |
| 2061 | + ggml_backend_buffer_type_t buft = node->buffer->buft; |
| 2062 | + if (backend && !ggml_backend_supports_buft(backend, buft)) { |
| 2063 | + color2 = simple_hash((const unsigned char *)ggml_backend_buft_name(buft)) % COLOR_NUM; |
| 2064 | + if (color2 == color1) color2 = (~color1) % COLOR_NUM; |
| 2065 | + } |
| 2066 | + } |
| 2067 | +#ifndef GGML_DOT_FULL_COLOR |
| 2068 | + snprintf(color, sizeof(color), "%d;0.5:%d", color1 + 1, color2 + 1); |
| 2069 | +#else |
| 2070 | + snprintf(color, sizeof(color), "#%06x;0.5:#%06x", color1, color2); |
| 2071 | +#endif |
| 2072 | + return color; |
| 2073 | +} |
| 2074 | + |
| 2075 | +static void ggml_graph_dump_dot_leaf(ggml_backend_sched_t sched, FILE * fp, std::set<void *> &visited_nodes, struct ggml_tensor * node, struct ggml_tensor *parent, const char *label, const int i) { |
| 2076 | + if (visited_nodes.find(node) != visited_nodes.end()) { |
| 2077 | + goto draw_edge; |
| 2078 | + } |
| 2079 | + |
| 2080 | + visited_nodes.insert(node); |
| 2081 | + |
| 2082 | + fprintf(fp, " \"%p\" [ style = filled; fillcolor = \"%s\"; shape = record; label=\"<x>", |
| 2083 | + (void *)node, ggml_color_of_backend(sched, node)); |
| 2084 | + |
| 2085 | + if (strlen(node->name) > 0) { |
| 2086 | + fprintf(fp, "%s (%s)|", node->name, ggml_type_name(node->type)); |
| 2087 | + } else { |
| 2088 | + fprintf(fp, "(%s)|", ggml_type_name(node->type)); |
| 2089 | + } |
| 2090 | + |
| 2091 | + fprintf(fp, "CONST %d [%" PRId64 ", %" PRId64 "]", i, node->ne[0], node->ne[1]); |
| 2092 | + fprintf(fp, "\"; ]\n"); |
| 2093 | + |
| 2094 | +draw_edge: |
| 2095 | + if (parent) { |
| 2096 | + ggml_graph_dump_dot_leaf_edge(fp, parent, node, label); |
| 2097 | + } |
| 2098 | + if (node->view_src) { |
| 2099 | + ggml_graph_dump_dot_leaf_edge(fp, node, node->view_src, label); |
| 2100 | + } |
| 2101 | +} |
| 2102 | + |
| 2103 | +static void ggml_graph_dump_dot_node(ggml_backend_sched_t sched, FILE * fp, std::set<void *> &visited_nodes, const struct ggml_cgraph * graph, struct ggml_tensor * node, struct ggml_tensor *child, const char *label, const int i); |
| 2104 | + |
| 2105 | +static void ggml_graph_dump_dot_real_node(ggml_backend_sched_t sched, FILE * fp, std::set<void *> &visited_nodes, const struct ggml_cgraph * graph, struct ggml_tensor * node, struct ggml_tensor *child, const char *label, const int i) { |
| 2106 | + char color[16]; |
| 2107 | + struct ggml_tensor * grad = nullptr; |
| 2108 | + |
| 2109 | + if (visited_nodes.find(node) != visited_nodes.end()) { |
| 2110 | + goto draw_edge; |
| 2111 | + } |
| 2112 | + |
| 2113 | + visited_nodes.insert(node); |
| 2114 | + |
| 2115 | + grad = ggml_graph_get_grad(graph, node); |
| 2116 | + |
| 2117 | + if (node->flags & GGML_TENSOR_FLAG_PARAM) { |
| 2118 | + snprintf(color, sizeof(color), "yellow"); |
| 2119 | + } else if (grad) { |
| 2120 | + snprintf(color, sizeof(color), "lightblue"); |
| 2121 | + } else { |
| 2122 | + snprintf(color, sizeof(color), "white"); |
| 2123 | + } |
| 2124 | + |
| 2125 | + fprintf(fp, " \"%p\" [ style = filled; fillcolor = \"%s\"; shape = record; label=\"", |
| 2126 | + (void *) node, ggml_color_of_backend(sched, node)); |
| 2127 | + |
| 2128 | + if (strlen(node->name) > 0) { |
| 2129 | + fprintf(fp, "%s (%s)|", node->name, ggml_type_name(node->type)); |
| 2130 | + } else { |
| 2131 | + fprintf(fp, "(%s)|", ggml_type_name(node->type)); |
| 2132 | + } |
| 2133 | + |
| 2134 | + if (ggml_is_matrix(node)) { |
| 2135 | + fprintf(fp, "%d [%" PRId64 ", %" PRId64 "] | <x>%s", i, node->ne[0], node->ne[1], ggml_op_symbol(node->op)); |
| 2136 | + } else { |
| 2137 | + fprintf(fp, "%d [%" PRId64 ", %" PRId64 ", %" PRId64 "] | <x>%s", i, node->ne[0], node->ne[1], node->ne[2], ggml_op_symbol(node->op)); |
| 2138 | + } |
| 2139 | + |
| 2140 | + if (grad) { |
| 2141 | + fprintf(fp, " | <g>%s\"; ]\n", ggml_op_symbol(grad->op)); |
| 2142 | + } else { |
| 2143 | + fprintf(fp, "\"; ]\n"); |
| 2144 | + } |
| 2145 | + |
| 2146 | + for (int j = 0; j < GGML_MAX_SRC; j++) { |
| 2147 | + if (node->src[j]) { |
| 2148 | + char label[16]; |
| 2149 | + snprintf(label, sizeof(label), "src %d", j); |
| 2150 | + ggml_graph_dump_dot_node(sched, fp, visited_nodes, graph, node->src[j], node, label, -1); |
| 2151 | + } |
| 2152 | + } |
| 2153 | + |
| 2154 | +draw_edge: |
| 2155 | + if (child) { |
| 2156 | + ggml_graph_dump_dot_node_edge(fp, graph, child, node, label); |
| 2157 | + } |
| 2158 | +} |
| 2159 | + |
| 2160 | +static void ggml_graph_dump_dot_node(ggml_backend_sched_t sched, FILE * fp, std::set<void *> &visited_nodes, const struct ggml_cgraph * graph, struct ggml_tensor * node, struct ggml_tensor *child, const char *label, const int i) { |
| 2161 | + if ((node->op == GGML_OP_NONE) && !(node->flags & GGML_TENSOR_FLAG_PARAM)) { |
| 2162 | + ggml_graph_dump_dot_leaf(sched, fp, visited_nodes, node, child, label, i); |
| 2163 | + } else { |
| 2164 | + ggml_graph_dump_dot_real_node(sched, fp, visited_nodes, graph, node, child, label, i); |
| 2165 | + } |
| 2166 | +} |
| 2167 | + |
| 2168 | +void ggml_backend_sched_dump_dot(ggml_backend_sched_t sched, const struct ggml_cgraph * graph, const char * filename) { |
| 2169 | + FILE * fp = ggml_fopen(filename, "w"); |
| 2170 | + GGML_ASSERT(fp); |
| 2171 | + |
| 2172 | + std::set<void *> visited_nodes; |
| 2173 | + |
| 2174 | + fprintf(fp, "digraph G {\n"); |
| 2175 | +#ifndef GGML_DOT_FULL_COLOR |
| 2176 | + fprintf(fp, "node [colorscheme=set312]\n"); |
| 2177 | +#endif |
| 2178 | + fprintf(fp, " newrank = true;\n"); |
| 2179 | + fprintf(fp, " rankdir = TB;\n"); |
| 2180 | + |
| 2181 | + for (int i = 0; i < graph->n_nodes; i++) { |
| 2182 | + struct ggml_tensor * node = graph->nodes[i]; |
| 2183 | + |
| 2184 | + if (ggml_graph_get_parent(graph, node)) { |
| 2185 | + continue; |
| 2186 | + } |
| 2187 | + |
| 2188 | + ggml_graph_dump_dot_node(sched, fp, visited_nodes, graph, node, NULL, NULL, i); |
| 2189 | + } |
| 2190 | + |
| 2191 | + ggml_backend_sched_splits_fdump_dot(fp, sched, graph); |
| 2192 | + |
| 2193 | + fprintf(fp, "}\n"); |
| 2194 | + fclose(fp); |
| 2195 | + |
| 2196 | + GGML_LOG_INFO("%s: dot -Tpng %s -o %s.png && open %s.png\n", __func__, filename, filename, filename); |
| 2197 | +} |
0 commit comments