Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 19 additions & 28 deletions BENCHMARKING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ Comprehensive guide to benchmarking Yog graph algorithms.
### Run the Example

```bash
gleam run -m internal/bench/simple_pathfinding
gleam run -m bench/simple_pathfinding
```

### Create Your Own

```bash
cp src/yog/internal/bench/simple_pathfinding.gleam src/yog/internal/bench/my_benchmark.gleam
cp test/bench/simple_pathfinding.gleam test/bench/my_benchmark.gleam
# Edit my_benchmark.gleam
gleam run -m internal/bench/my_benchmark
gleam run -m bench/my_benchmark
```

### Basic Template
Expand Down Expand Up @@ -147,7 +147,7 @@ bench.run(inputs, functions, [bench.Duration(2000)])

Benchmarks display a table with these metrics:

```
```shell
Input Function IPS Min Max P99
Small: 100 nodes Dijkstra 3906736.16 0.0001 56.02 0.001
Medium: 1K nodes Dijkstra 557.99 1.4301 5.42 4.20
Expand All @@ -170,14 +170,12 @@ Medium: 1K nodes Dijkstra 557.99 1.4301 5.42 4.20
### Expected Performance

| Algorithm | Complexity | 100 nodes | 1K nodes | 10K nodes |
|-----------|-----------|-----------|----------|-----------|
| ----------- | ----------- | ----------- | ---------- | ----------- |
| BFS/DFS | O(V+E) | ~100μs | ~1ms | ~10ms |
| Dijkstra | O((V+E) log V) | ~200μs | ~2-3ms | ~30-40ms |
| Bellman-Ford | O(VE) | ~500μs | ~50ms | ~5s |
| Floyd-Warshall | O(V³) | ~10ms | ~10s | Too slow |

*Rough estimates for sparse graphs*

## Best Practices

### 1. Choose Appropriate Sizes
Expand Down Expand Up @@ -247,7 +245,7 @@ io.println("Complexity: O(E log V) vs O(VE)\n")
## Algorithm Complexity Reference

| Algorithm | Time | Space | Max Practical Size |
|-----------|------|-------|--------------------|
| ----------- | ------ | ------- | -------------------- |
| BFS/DFS | O(V+E) | O(V) | 100K+ nodes |
| Dijkstra | O((V+E) log V) | O(V) | 100K+ nodes |
| A* | O((V+E) log V) | O(V) | 100K+ nodes |
Expand All @@ -263,57 +261,50 @@ io.println("Complexity: O(E log V) vs O(VE)\n")
## Troubleshooting

### Benchmark takes forever

- Reduce graph size
- Lower `Duration` parameter
- Check algorithm complexity vs graph size

### Out of memory

- Use smaller graphs
- Avoid `Dense` with `Large` or `XLarge`
- Check algorithm space complexity

### Inconsistent results

- Increase `Duration` and `Warmup`
- Close other applications
- Run multiple times

### Module not found
- File must be in `src/yog/internal/bench/`
- Run: `gleam run -m internal/bench/filename`

## Why `internal/bench`?

Benchmarks are in `src/yog/internal/bench/` to:
- Keep them out of Yog's public API
- Prevent pollution of user imports/autocomplete
- Still allow individual execution: `gleam run -m internal/bench/...`
- File must be in `test/bench/`
- Run: `gleam run -m bench/filename`

This follows Gleam best practices for internal tooling.

## Why `bench_erlang/` (and not in `src/`)?
## Why `bench_erlang/` (and not in `test/bench`)?

The `bench_erlang/` directory at the project root contains benchmarks that compare Yog with Erlang's `:digraph` module using FFI (Foreign Function Interface). These files can't live in `src/` because:

### The Multi-Target Problem

Yog supports both **Erlang** and **JavaScript** targets (`targets = ["erlang", "javascript"]` in `gleam.toml`). When you run `gleam build --target javascript`, Gleam compiles everything in `src/` - and Erlang FFI code doesn't exist in JavaScript, causing compilation to fail.

### Why Not Just Remove JS Support?

We wanted Yog to be usable in both ecosystems. JavaScript developers deserve good graph libraries too!

### The Solution

Keep target-specific benchmarks **outside** `src/`:
- ✅ `src/` compiles on all targets (Erlang + JS)
Keep target-specific benchmarks **outside** `test/`:

- ✅ `test/` compiles on all targets (Erlang + JS)
- ✅ `bench_erlang/` preserves powerful Erlang comparisons
- ✅ Files keep `.gleam` extension (IDE support, syntax highlighting)
- ✅ Gleam ignores them (only compiles `src/`)
- ✅ Copy when needed: `cp bench_erlang/compare_digraph_acyclic.gleam src/yog/internal/bench/`
- ✅ Gleam ignores them (only compiles `test/`)
- ✅ Copy when needed: `cp bench_erlang/compare_digraph_acyclic.gleam test/bench/`

### Future: `bench_javascript/`?

Following this pattern, we could add `bench_javascript/` for comparing Yog against popular JS libraries like:

- **graphology** - Comprehensive graph library
- **graphlib** - Used by Mermaid, D3, Dagre
- **js-graph-algorithms** - Pure algorithm implementations
Expand All @@ -328,4 +319,4 @@ This structure lets us maintain comprehensive, target-specific benchmarks while

---

**Happy Benchmarking! 📊**
### **Happy Benchmarking! 📊**
62 changes: 24 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,45 +99,37 @@ We have some real-world projects that use Yog for graph algorithms:
- [Lustre Graph Generator](https://github.com/code-shoily/lustre_graph_generator) ([Demo](https://code-shoily.github.io/lustre_graph_generator/)) - Showcases graph generation, topological sort and shortest distance feature of Yog.
- [Advent of Code Solutions](https://github.com/code-shoily/aocgl/blob/main/wiki/tags/graph.md) - Multiple AoC puzzles solved using Yog's graph capabilities.

Detailed examples are located in the [examples/](https://github.com/code-shoily/yog/tree/main/examples) directory:

- [Social Network Analysis](https://github.com/code-shoily/yog/blob/main/examples/social_network_analysis.gleam) - Finding communities using SCCs.
- [Task Scheduling](https://github.com/code-shoily/yog/blob/main/examples/task_scheduling.gleam) - Basic topological sorting.
- [GPS Navigation](https://github.com/code-shoily/yog/blob/main/examples/gps_navigation.gleam) - Shortest path using A* and heuristics.
- [Network Cable Layout](https://github.com/code-shoily/yog/blob/main/examples/network_cable_layout.gleam) - Minimum Spanning Tree using Kruskal's.
- [Network Bandwidth](https://github.com/code-shoily/yog/blob/main/examples/network_bandwidth.gleam) - ⭐ Max flow for bandwidth optimization with bottleneck analysis.
- [Job Matching](https://github.com/code-shoily/yog/blob/main/examples/job_matching.gleam) - ⭐ Max flow for bipartite matching and assignment problems.
- [Cave Path Counting](https://github.com/code-shoily/yog/blob/main/examples/cave_path_counting.gleam) - Custom DFS with backtracking.
- [Task Ordering](https://github.com/code-shoily/yog/blob/main/examples/task_ordering.gleam) - Lexicographical topological sort.
- [Bridges of Königsberg](https://github.com/code-shoily/yog/blob/main/examples/bridges_of_konigsberg.gleam) - Eulerian circuit and path detection.
- [Global Minimum Cut](https://github.com/code-shoily/yog/blob/main/examples/global_min_cut.gleam) - Stoer-Wagner algorithm.
- [Job Assignment](https://github.com/code-shoily/yog/blob/main/examples/job_assignment.gleam) - Bipartite maximum matching.
- [Medical Residency](https://github.com/code-shoily/yog/blob/main/examples/medical_residency.gleam) - Stable marriage matching (Gale-Shapley algorithm).
- [City Distance Matrix](https://github.com/code-shoily/yog/blob/main/examples/city_distance_matrix.gleam) - Floyd-Warshall for all-pairs shortest paths.
- [Graph Generation Showcase](https://github.com/code-shoily/yog/blob/main/examples/graph_generation_showcase.gleam) - ⭐ All 9 classic graph patterns with statistics.
- [DOT rendering](https://github.com/code-shoily/yog/blob/main/examples/render_dot.gleam) - Exporting graphs to Graphviz format.
- [Mermaid rendering](https://github.com/code-shoily/yog/blob/main/examples/render_mermaid.gleam) - Generating Mermaid diagrams.
- [JSON rendering](https://github.com/code-shoily/yog/blob/main/examples/render_json.gleam) - Exporting graphs to JSON for web use.
- [Graph creation](https://github.com/code-shoily/yog/blob/main/examples/graph_creation.gleam) - Comprehensive guide to 10+ ways of creating graphs.
Detailed examples are located in the [test/examples/](https://github.com/code-shoily/yog/tree/main/test/examples) directory:

- [Social Network Analysis](https://github.com/code-shoily/yog/blob/main/test/examples/social_network_analysis.gleam) - Finding communities using SCCs.
- [Task Scheduling](https://github.com/code-shoily/yog/blob/main/test/examples/task_scheduling.gleam) - Basic topological sorting.
- [GPS Navigation](https://github.com/code-shoily/yog/blob/main/test/examples/gps_navigation.gleam) - Shortest path using A* and heuristics.
- [Network Cable Layout](https://github.com/code-shoily/yog/blob/main/test/examples/network_cable_layout.gleam) - Minimum Spanning Tree using Kruskal's.
- [Network Bandwidth](https://github.com/code-shoily/yog/blob/main/test/examples/network_bandwidth.gleam) - ⭐ Max flow for bandwidth optimization with bottleneck analysis.
- [Job Matching](https://github.com/code-shoily/yog/blob/main/test/examples/job_matching.gleam) - ⭐ Max flow for bipartite matching and assignment problems.
- [Cave Path Counting](https://github.com/code-shoily/yog/blob/main/test/examples/cave_path_counting.gleam) - Custom DFS with backtracking.
- [Task Ordering](https://github.com/code-shoily/yog/blob/main/test/examples/task_ordering.gleam) - Lexicographical topological sort.
- [Bridges of Königsberg](https://github.com/code-shoily/yog/blob/main/test/examples/bridges_of_konigsberg.gleam) - Eulerian circuit and path detection.
- [Global Minimum Cut](https://github.com/code-shoily/yog/blob/main/test/examples/global_min_cut.gleam) - Stoer-Wagner algorithm.
- [Job Assignment](https://github.com/code-shoily/yog/blob/main/test/examples/job_assignment.gleam) - Bipartite maximum matching.
- [Medical Residency](https://github.com/code-shoily/yog/blob/main/test/examples/medical_residency.gleam) - Stable marriage matching (Gale-Shapley algorithm).
- [City Distance Matrix](https://github.com/code-shoily/yog/blob/main/test/examples/city_distance_matrix.gleam) - Floyd-Warshall for all-pairs shortest paths.
- [Graph Generation Showcase](https://github.com/code-shoily/yog/blob/main/test/examples/graph_generation_showcase.gleam) - ⭐ All 9 classic graph patterns with statistics.
- [DOT rendering](https://github.com/code-shoily/yog/blob/main/test/examples/render_dot.gleam) - Exporting graphs to Graphviz format.
- [Mermaid rendering](https://github.com/code-shoily/yog/blob/main/test/examples/render_mermaid.gleam) - Generating Mermaid diagrams.
- [JSON rendering](https://github.com/code-shoily/yog/blob/main/test/examples/render_json.gleam) - Exporting graphs to JSON for web use.
- [Graph creation](https://github.com/code-shoily/yog/blob/main/test/examples/graph_creation.gleam) - Comprehensive guide to 10+ ways of creating graphs.

### Running Examples Locally

The examples live in the `examples/` directory. To run them with `gleam run`, create a one-time symlink that makes Gleam's module system aware of them:
The examples live in the `test/examples/` directory and can be run directly:

```sh
ln -sf "$(pwd)/examples" src/yog/internal/examples
```

Then run any example by its module name:

```sh
gleam run -m yog/internal/examples/gps_navigation
gleam run -m yog/internal/examples/network_bandwidth
gleam run -m examples/gps_navigation
gleam run -m examples/network_bandwidth
# etc.
```

> The symlink is listed in `.gitignore` and is not committed to the repository, so it won't affect CI or other contributors' environments.

## Algorithm Selection Guide

Detailed documentation for each algorithm can be found on [HexDocs](https://hexdocs.pm/yog/).
Expand Down Expand Up @@ -169,13 +161,7 @@ Detailed documentation for each algorithm can be found on [HexDocs](https://hexd
Yog includes built-in benchmarking utilities using `gleamy/bench`. Run the example benchmark:

```bash
gleam run -m internal/bench/simple_pathfinding
```

Or use the provided script to run all benchmarks:

```bash
./run_benchmarks.sh
gleam run -m bench/simple_pathfinding
```

For detailed instructions on creating custom benchmarks, interpreting results, and comparing against reference implementations, see the [Benchmarking Guide](BENCHMARKING_GUIDE.md).
Expand Down
11 changes: 7 additions & 4 deletions bench_erlang/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@ These benchmarks use Erlang's `:digraph` module via FFI, which is only available

## Running a Benchmark

1. Copy the benchmark to `src/yog/internal/bench/`:
1. Copy the benchmark to `test/bench/`:

```bash
cp bench_erlang/compare_digraph_acyclic.gleam src/yog/internal/bench/
cp bench_erlang/compare_digraph_acyclic.gleam test/bench/
```

2. Run it:

```bash
gleam run -m internal/bench/compare_digraph_acyclic
gleam run -m test/compare_digraph_acyclic
```

3. Clean up when done:

```bash
rm src/yog/internal/bench/compare_digraph_acyclic.gleam
rm test/bench/compare_digraph_acyclic.gleam
```

## Note
Expand Down
12 changes: 5 additions & 7 deletions bench_erlang/compare_digraph.gleam
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
//// Yog vs Erlang digraph Comparison Benchmark
////
//// Compares Yog's SCC implementation against Erlang's built-in digraph
////
//// This benchmark is Erlang-only and must be copied to src/yog/internal/bench/ first:
//// cp bench_erlang/compare_digraph.gleam src/yog/internal/bench/
//// gleam run -m internal/bench/compare_digraph
//// rm src/yog/internal/bench/compare_digraph.gleam
//// Run this benchmark with: `gleam run -m bench/compare_digraph`
////
//// *Please Note:* You will need to move this file to test/bench before running
//// the command above.

import bench/bench_utils
import gleam/dict
import gleam/io
import gleam/list
import gleamy/bench
import yog/connectivity
import yog/internal/bench/bench_utils
import yog/model.{type Graph}

// Erlang digraph FFI
Expand Down
12 changes: 6 additions & 6 deletions bench_erlang/compare_digraph_acyclic.gleam
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//// Yog vs Erlang digraph - Acyclic Check Comparison
//// Yog vs Erlang digraph - Acyclic Check Comparison
////
//// This benchmark is Erlang-only and must be copied to src/yog/internal/bench/ first:
//// cp bench_erlang/compare_digraph_acyclic.gleam src/yog/internal/bench/
//// gleam run -m internal/bench/compare_digraph_acyclic
//// rm src/yog/internal/bench/compare_digraph_acyclic.gleam
//// Run this benchmark with: `gleam run -m bench/compare_digraph_acyclic`
////
//// *Please Note:* You will need to move this file to test/bench before running
//// the command above.

import bench/bench_utils
import gleam/dict
import gleam/io
import gleam/list
import gleamy/bench
import yog/internal/bench/bench_utils
import yog/model.{type Graph}
import yog/traversal

Expand Down
10 changes: 5 additions & 5 deletions bench_erlang/compare_digraph_condensation.gleam
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
//// Yog vs Erlang digraph - Condensation Comparison
////
//// This benchmark is Erlang-only and must be copied to src/yog/internal/bench/ first:
//// cp bench_erlang/compare_digraph_condensation.gleam src/yog/internal/bench/
//// gleam run -m internal/bench/compare_digraph_condensation
//// rm src/yog/internal/bench/compare_digraph_condensation.gleam
//// Run this benchmark with: `gleam run -m bench/compare_digraph_condensation
////
//// *Please Note:* You will need to move this file to test/bench before running
//// the command above.

import bench/bench_utils
import gleam/dict
import gleam/io
import gleam/list
import gleamy/bench
import yog/connectivity
import yog/internal/bench/bench_utils
import yog/model.{type Graph}

// Erlang digraph FFI
Expand Down
10 changes: 5 additions & 5 deletions bench_erlang/compare_digraph_cycle.gleam
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//// Yog vs Erlang digraph - Cycle Detection Comparison
////
//// This benchmark is Erlang-only and must be copied to src/yog/internal/bench/ first:
//// cp bench_erlang/compare_digraph_cycle.gleam src/yog/internal/bench/
//// gleam run -m internal/bench/compare_digraph_cycle
//// rm src/yog/internal/bench/compare_digraph_cycle.gleam
//// Run this benchmark with: `gleam run -m bench/compare_digraph_cycle`
////
//// *Please Note:* You will need to move this file to test/bench before running
//// the command above.

import bench/bench_utils
import gleam/dict
import gleam/io
import gleam/list
import gleamy/bench
import yog/internal/bench/bench_utils
import yog/model.{type Graph}
import yog/traversal

Expand Down
12 changes: 6 additions & 6 deletions bench_erlang/compare_digraph_path.gleam
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
//// Yog vs Erlang digraph - Shortest Path Comparison
////
//// This benchmark is Erlang-only and must be copied to src/yog/internal/bench/ first:
//// cp bench_erlang/compare_digraph_path.gleam src/yog/internal/bench/
//// gleam run -m internal/bench/compare_digraph_path
//// rm src/yog/internal/bench/compare_digraph_path.gleam
//// Run this benchmark with: `gleam run -m bench/compare_digraph_path`
////
//// *Please Note:* You will need to move this file to test/bench before running
//// the command above.

import bench/bench_utils
import gleam/dict
import gleam/int
import gleam/io
import gleam/list
import gleamy/bench
import yog/internal/bench/bench_utils
import yog/model.{type Graph}
import yog/pathfinding
import yog/pathfinding/dijkstra as pathfinding

// Erlang digraph FFI
@external(erlang, "digraph", "new")
Expand Down
10 changes: 5 additions & 5 deletions bench_erlang/compare_digraph_reachability.gleam
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//// Yog vs Erlang digraph - Reachability Comparison
////
//// This benchmark is Erlang-only and must be copied to src/yog/internal/bench/ first:
//// cp bench_erlang/compare_digraph_reachability.gleam src/yog/internal/bench/
//// gleam run -m internal/bench/compare_digraph_reachability
//// rm src/yog/internal/bench/compare_digraph_reachability.gleam
//// Run this benchmark with: `gleam run -m bench/compare_digraph_reachability`
////
//// *Please Note:* You will need to move this file to test/bench before running
//// the command above.

import bench/bench_utils
import gleam/dict
import gleam/io
import gleam/list
import gleamy/bench
import yog/internal/bench/bench_utils
import yog/model.{type Graph}
import yog/traversal

Expand Down
10 changes: 5 additions & 5 deletions bench_erlang/compare_digraph_topsort.gleam
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//// Yog vs Erlang digraph - Topological Sort Comparison
////
//// This benchmark is Erlang-only and must be copied to src/yog/internal/bench/ first:
//// cp bench_erlang/compare_digraph_topsort.gleam src/yog/internal/bench/
//// gleam run -m internal/bench/compare_digraph_topsort
//// rm src/yog/internal/bench/compare_digraph_topsort.gleam
//// Run this benchmark with: `gleam run -m bench/compare_digraph_topsort`
////
//// *Please Note:* You will need to move this file to test/bench before running
//// the command above.

import bench/bench_utils
import gleam/dict
import gleam/io
import gleam/list
import gleamy/bench
import yog/internal/bench/bench_utils
import yog/model.{type Graph}
import yog/traversal

Expand Down
Loading
Loading