main.c (5981B)
1 #define _GNU_SOURCE 1 2 3 #include <assert.h> 4 #include <inttypes.h> 5 #include <stdalign.h> 6 #include <stdarg.h> 7 #include <stddef.h> 8 #include <stdlib.h> 9 #include <stdint.h> 10 #include <stdio.h> 11 #include <string.h> 12 #include <time.h> 13 14 // helper definitions 15 16 #define MSECS 1000ull 17 #define USECS 1000000ull 18 #define NSECS 1000000000ull 19 20 #define KiB(v) (1024ull * (v)) 21 #define MiB(v) (1024 * KiB(v)) 22 #define GiB(v) (1024 * MiB(v)) 23 24 #define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0]) 25 26 #define ISPOW2(v) (((v) & ((v) - 1)) == 0) 27 #define IS_ALIGNED(v, a) (((v) * ((a) - 1)) == 0) 28 #define ALIGN_PREV(v, a) ((v) & ~((a) - 1)) 29 #define ALIGN_NEXT(v, a) ALIGN_PREV((v) + ((a) - 1), (a)) 30 31 struct arena { 32 void *ptr; 33 size_t cap, len; 34 }; 35 36 static void 37 arena_reset(struct arena *arena) 38 { 39 arena->len = 0; 40 } 41 42 static void * 43 arena_alloc(struct arena *arena, size_t size, size_t align) 44 { 45 assert(ISPOW2(align)); 46 assert(size); 47 48 uintptr_t begin = (uintptr_t) arena->ptr; 49 uintptr_t end = begin + arena->cap; 50 51 uintptr_t aligned = ALIGN_NEXT(begin + arena->len, align); 52 if (aligned + size > end) 53 return NULL; 54 55 arena->len = (aligned - begin) + size; 56 57 return (void *) aligned; 58 } 59 60 #define PUSH_ARRAY(arena, T, n) \ 61 arena_alloc((arena), sizeof(T) * (n), alignof(T)) 62 #define PUSH_SIZED(arena, T) PUSH_ARRAY((arena), T, 1) 63 64 static uintmax_t 65 nanosecs(void) 66 { 67 struct timespec now; 68 clock_gettime(CLOCK_MONOTONIC_RAW, &now); 69 70 return (uintmax_t) (now.tv_sec * NSECS) + now.tv_nsec; 71 } 72 73 // graph definitions 74 75 #define MAX_EDGE_WEIGHT 10000 76 #define EDGES_PER_NODE 10 77 78 struct node; 79 80 struct edge { 81 struct node *src, *dst; 82 struct edge *next; 83 double w; 84 }; 85 86 struct node { 87 struct edge *edges; 88 }; 89 90 struct graph { 91 struct node *nodes; 92 size_t nodes_count; 93 94 struct edge *edges; 95 size_t edges_count; 96 }; 97 98 struct graph 99 random_graph(struct arena *scratch, size_t nodes, size_t edges_per_node) 100 { 101 assert(nodes); 102 assert(edges_per_node); 103 assert(edges_per_node <= nodes); 104 105 struct graph graph; 106 memset(&graph, 0, sizeof graph); 107 108 graph.nodes_count = nodes; 109 graph.edges_count = nodes * edges_per_node; 110 111 // allocate memory for graph 112 graph.nodes = PUSH_ARRAY(scratch, struct node, graph.nodes_count); 113 assert(graph.nodes); 114 115 graph.edges = PUSH_ARRAY(scratch, struct edge, graph.edges_count); 116 assert(graph.edges); 117 118 // initialise graph nodes and edges 119 struct edge *edge = &graph.edges[0]; 120 for (size_t n = 0; n < graph.nodes_count; n++) { 121 struct node *node = &graph.nodes[n]; 122 node->edges = NULL; 123 124 for (size_t e = 0; e < edges_per_node; e++) { 125 edge->src = node; 126 127 do { 128 edge->dst = &graph.nodes[random() % graph.nodes_count]; 129 } while (edge->dst == edge->src); 130 131 edge->next = node->edges; 132 edge->w = (double) (random() % MAX_EDGE_WEIGHT); 133 134 node->edges = edge; 135 136 edge++; 137 }; 138 } 139 140 return graph; 141 } 142 143 // algorithms under test 144 145 typedef int (*sssp_t)(struct arena *scratch, struct graph *graph, 146 struct node const *src, double *dists, va_list ap); 147 148 int 149 dijkstra(struct arena *scratch, struct graph *graph, 150 struct node const *src, double *dists, va_list ap); 151 152 int 153 bellman_ford(struct arena *scratch, struct graph *graph, 154 struct node const *src, double *dists, va_list ap); 155 156 int 157 dmmsy(struct arena *scratch, struct graph *graph, 158 struct node const *src, double *dists, va_list ap); 159 160 // benchmarking harness 161 162 size_t graph_sizes[] = { 163 1000, 164 10000, 165 100000, 166 1000000, 167 }; 168 169 struct { 170 char *name; 171 sssp_t algo; 172 } algorithms[] = { 173 { "dijkstra", dijkstra, }, 174 { "bellman-ford", bellman_ford, }, 175 { "dmmsy", dmmsy, }, 176 }; 177 178 static uintmax_t 179 bench(struct arena *arena, size_t nodes, sssp_t algorithm, double distances[nodes], ...) 180 { 181 va_list ap; 182 va_start(ap, distances); 183 184 arena_reset(arena); 185 186 // seed random to ensure identical graphs between algorithms 187 srandom(nodes); 188 189 struct graph graph = random_graph(arena, nodes, EDGES_PER_NODE); 190 191 struct node *src = graph.nodes + (random() % graph.nodes_count); 192 uintmax_t begin = nanosecs(); 193 194 int res = algorithm(arena, &graph, src, distances, ap); 195 if (res < 0) { 196 return -1; 197 } 198 199 uintmax_t end = nanosecs(); 200 201 va_end(ap); 202 203 return end - begin; 204 } 205 206 int 207 main(void) 208 { 209 for (size_t i = 0; i < ARRLEN(graph_sizes); i++) { 210 // calculate bounds for our memory 211 size_t max_nodes = graph_sizes[i]; 212 size_t distances = max_nodes; 213 size_t max_edges = max_nodes * EDGES_PER_NODE; 214 size_t algorithm_scratch_space = MiB(16); // TODO: what is the limit? 215 uintmax_t max_memory = sizeof(struct node) * max_nodes 216 + sizeof(double) * distances 217 + sizeof(struct edge) * max_edges 218 + algorithm_scratch_space; 219 220 printf("======================================================================\n"); 221 printf("max nodes: %11zu, max edges: %11zu, max memory: %" PRIuMAX "\n", 222 max_nodes, max_edges, max_memory); 223 printf("======================================================================\n"); 224 225 struct arena arena; 226 arena.cap = max_memory; 227 arena.ptr = malloc(arena.cap); 228 assert(arena.ptr); 229 230 // find true distances 231 double *golden = malloc(sizeof *golden * distances); 232 assert(golden); 233 memset(golden, 0, sizeof *golden * distances); 234 235 (void) bench(&arena, max_nodes, dijkstra, golden); 236 237 for (size_t j = 0; j < ARRLEN(algorithms); j++) { 238 char *name = algorithms[j].name; 239 sssp_t algo = algorithms[j].algo; 240 241 double *results = PUSH_ARRAY(&arena, double, distances); 242 assert(results); 243 memset(results, 0, sizeof *results * distances); 244 245 uintmax_t elapsed_ns = bench(&arena, max_nodes, algo, results); 246 uintmax_t elapsed_ms = (elapsed_ns * MSECS) / NSECS; 247 248 int res = memcmp(results, golden, sizeof *results * distances); 249 if (res == 0) { 250 printf("algorithm: %20s, " 251 "elapsed ns: %11" PRIuMAX ", " 252 "elapsed ms: %11" PRIuMAX "\n", 253 name, elapsed_ns, elapsed_ms); 254 } else { 255 printf("algorithm: %20s, RESULT MISMATCH!\n", 256 name); 257 } 258 } 259 260 free(golden); 261 free(arena.ptr); 262 } 263 264 return 0; 265 } 266 267 #include "dijkstra.c" 268 #include "bellman_ford.c" 269 #include "dmmsy.c"