sssp

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

commit a8652f8cf6db80450a5663292d85fc3501985455
Author: Mikołaj Lenczewski <mblenczewski@gmail.com>
Date:   Mon, 23 Feb 2026 22:10:30 +0000

Initial commit

Diffstat:
A.gitignore | 4++++
ALICENSE | 18++++++++++++++++++
AREADME | 3+++
Abellman_ford.c | 6++++++
Abuild.sh | 5+++++
Aclean.sh | 5+++++
Adijkstra.c | 6++++++
Admmsy.c | 6++++++
Amain.c | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 322 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,4 @@ +sssp + +**/.*.swp +tags diff --git a/LICENSE b/LICENSE @@ -0,0 +1,18 @@ +The MIT-Zero License + +Copyright (c) 2026 Mikołaj Lenczewski <mikolaj@lenczewski.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README b/README @@ -0,0 +1,3 @@ +SSSP Benchmarking +============================================================================== +Benchmarking Dijkstra's algorithm, Bellman-Ford, and DMMSY on sparse graphs. diff --git a/bellman_ford.c b/bellman_ford.c @@ -0,0 +1,6 @@ +int +bellman_ford(struct arena *scratch, struct graph *graph, + struct node const *src, double *dists, va_list ap) +{ + return -1; +} diff --git a/build.sh b/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -ex + +cc -o sssp main.c -O0 -g3 -Wall -Wextra -lm diff --git a/clean.sh b/clean.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -ex + +rm -rf sssp diff --git a/dijkstra.c b/dijkstra.c @@ -0,0 +1,6 @@ +int +dijkstra(struct arena *scratch, struct graph *graph, + struct node const *src, double *dists, va_list ap) +{ + return -1; +} diff --git a/dmmsy.c b/dmmsy.c @@ -0,0 +1,6 @@ +int +dmmsy(struct arena *scratch, struct graph *graph, + struct node const *src, double *dists, va_list ap) +{ + return -1; +} diff --git a/main.c b/main.c @@ -0,0 +1,269 @@ +#define _GNU_SOURCE 1 + +#include <assert.h> +#include <inttypes.h> +#include <stdalign.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +// helper definitions + +#define MSECS 1000ull +#define USECS 1000000ull +#define NSECS 1000000000ull + +#define KiB(v) (1024ull * (v)) +#define MiB(v) (1024 * KiB(v)) +#define GiB(v) (1024 * MiB(v)) + +#define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0]) + +#define ISPOW2(v) (((v) & ((v) - 1)) == 0) +#define IS_ALIGNED(v, a) (((v) * ((a) - 1)) == 0) +#define ALIGN_PREV(v, a) ((v) & ~((a) - 1)) +#define ALIGN_NEXT(v, a) ALIGN_PREV((v) + ((a) - 1), (a)) + +struct arena { + void *ptr; + size_t cap, len; +}; + +static void +arena_reset(struct arena *arena) +{ + arena->len = 0; +} + +static void * +arena_alloc(struct arena *arena, size_t size, size_t align) +{ + assert(ISPOW2(align)); + assert(size); + + uintptr_t begin = (uintptr_t) arena->ptr; + uintptr_t end = begin + arena->cap; + + uintptr_t aligned = ALIGN_NEXT(begin + arena->len, align); + if (aligned + size > end) + return NULL; + + arena->len = (aligned - begin) + size; + + return (void *) aligned; +} + +#define PUSH_ARRAY(arena, T, n) \ + arena_alloc((arena), sizeof(T) * (n), alignof(T)) +#define PUSH_SIZED(arena, T) PUSH_ARRAY((arena), T, 1) + +static uintmax_t +nanosecs(void) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC_RAW, &now); + + return (uintmax_t) (now.tv_sec * NSECS) + now.tv_nsec; +} + +// graph definitions + +#define MAX_EDGE_WEIGHT 10000 +#define EDGES_PER_NODE 10 + +struct node; + +struct edge { + struct node *src, *dst; + struct edge *next; + double w; +}; + +struct node { + struct edge *edges; +}; + +struct graph { + struct node *nodes; + size_t nodes_count; + + struct edge *edges; + size_t edges_count; +}; + +struct graph +random_graph(struct arena *scratch, size_t nodes, size_t edges_per_node) +{ + assert(nodes); + assert(edges_per_node); + assert(edges_per_node <= nodes); + + struct graph graph; + memset(&graph, 0, sizeof graph); + + graph.nodes_count = nodes; + graph.edges_count = nodes * edges_per_node; + + // allocate memory for graph + graph.nodes = PUSH_ARRAY(scratch, struct node, graph.nodes_count); + assert(graph.nodes); + + graph.edges = PUSH_ARRAY(scratch, struct edge, graph.edges_count); + assert(graph.edges); + + // initialise graph nodes and edges + struct edge *edge = &graph.edges[0]; + for (size_t n = 0; n < graph.nodes_count; n++) { + struct node *node = &graph.nodes[n]; + node->edges = NULL; + + for (size_t e = 0; e < edges_per_node; e++) { + edge->src = node; + + do { + edge->dst = &graph.nodes[random() % graph.nodes_count]; + } while (edge->dst == edge->src); + + edge->next = node->edges; + edge->w = (double) (random() % MAX_EDGE_WEIGHT); + + node->edges = edge; + + edge++; + }; + } + + return graph; +} + +// algorithms under test + +typedef int (*sssp_t)(struct arena *scratch, struct graph *graph, + struct node const *src, double *dists, va_list ap); + +int +dijkstra(struct arena *scratch, struct graph *graph, + struct node const *src, double *dists, va_list ap); + +int +bellman_ford(struct arena *scratch, struct graph *graph, + struct node const *src, double *dists, va_list ap); + +int +dmmsy(struct arena *scratch, struct graph *graph, + struct node const *src, double *dists, va_list ap); + +// benchmarking harness + +size_t graph_sizes[] = { + 1000, + 10000, + 100000, + 1000000, +}; + +struct { + char *name; + sssp_t algo; +} algorithms[] = { + { "dijkstra", dijkstra, }, + { "bellman-ford", bellman_ford, }, + { "dmmsy", dmmsy, }, +}; + +static uintmax_t +bench(struct arena *arena, size_t nodes, sssp_t algorithm, double distances[nodes], ...) +{ + va_list ap; + va_start(ap, distances); + + arena_reset(arena); + + // seed random to ensure identical graphs between algorithms + srandom(nodes); + + struct graph graph = random_graph(arena, nodes, EDGES_PER_NODE); + + struct node *src = graph.nodes + (random() % graph.nodes_count); + uintmax_t begin = nanosecs(); + + int res = algorithm(arena, &graph, src, distances, ap); + if (res < 0) { + return -1; + } + + uintmax_t end = nanosecs(); + + va_end(ap); + + return end - begin; +} + +int +main(void) +{ + for (size_t i = 0; i < ARRLEN(graph_sizes); i++) { + // calculate bounds for our memory + size_t max_nodes = graph_sizes[i]; + size_t distances = max_nodes; + size_t max_edges = max_nodes * EDGES_PER_NODE; + size_t algorithm_scratch_space = MiB(16); // TODO: what is the limit? + uintmax_t max_memory = sizeof(struct node) * max_nodes + + sizeof(double) * distances + + sizeof(struct edge) * max_edges + + algorithm_scratch_space; + + printf("======================================================================\n"); + printf("max nodes: %11zu, max edges: %11zu, max memory: %" PRIuMAX "\n", + max_nodes, max_edges, max_memory); + printf("======================================================================\n"); + + struct arena arena; + arena.cap = max_memory; + arena.ptr = malloc(arena.cap); + assert(arena.ptr); + + // find true distances + double *golden = malloc(sizeof *golden * distances); + assert(golden); + memset(golden, 0, sizeof *golden * distances); + + (void) bench(&arena, max_nodes, dijkstra, golden); + + for (size_t j = 0; j < ARRLEN(algorithms); j++) { + char *name = algorithms[j].name; + sssp_t algo = algorithms[j].algo; + + double *results = PUSH_ARRAY(&arena, double, distances); + assert(results); + memset(results, 0, sizeof *results * distances); + + uintmax_t elapsed_ns = bench(&arena, max_nodes, algo, results); + uintmax_t elapsed_ms = (elapsed_ns * MSECS) / NSECS; + + int res = memcmp(results, golden, sizeof *results * distances); + if (res == 0) { + printf("algorithm: %20s, " + "elapsed ns: %11" PRIuMAX ", " + "elapsed ms: %11" PRIuMAX "\n", + name, elapsed_ns, elapsed_ms); + } else { + printf("algorithm: %20s, RESULT MISMATCH!\n", + name); + } + } + + free(golden); + free(arena.ptr); + } + + return 0; +} + +#include "dijkstra.c" +#include "bellman_ford.c" +#include "dmmsy.c"