sssp

sssp.git
git clone git://git.lenczewski.org/sssp.git
Log | Files | Refs | README | LICENSE

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"