Skip to content

Commit c3a58c4

Browse files
Fixed memory leak in ultragraph bench (#261)
* Updated docstring Signed-off-by: Marvin Hansen <[email protected]> * Fixed a memory leak in the ultragraph benchmark. Signed-off-by: Marvin Hansen <[email protected]> * Fixed typo in Rustdoc. Signed-off-by: Marvin Hansen <[email protected]> --------- Signed-off-by: Marvin Hansen <[email protected]>
1 parent ece135d commit c3a58c4

File tree

4 files changed

+74
-60
lines changed

4 files changed

+74
-60
lines changed

deep_causality/src/types/reasoning_types/propagating_effect.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ pub enum PropagatingEffect {
2323
Probabilistic(NumericalValue),
2424

2525
/// A propagating effect that directs the flow of causality.
26-
///
27-
/// This instructs the reasoning engine to continue the propagation to the
28-
/// Causaloid located at the linked Contextoid. The linked Contextoid
29-
/// itself becomes the `Evidence` for the next step in the process.
26+
/// It instructs the next Causaloid to locate the propagation effect
27+
/// at the linked Contextoid. The linked Contextoid
28+
/// itself becomes the `Evidence` for the next step in the reasoning process.
3029
/// This is the primary mechanism for data flow in the graph.
3130
ContextualLink(ContextId, ContextoidId),
3231

ultragraph/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,36 @@ into a `DynamicGraph`, allowing the cycle of mutation and analysis to begin agai
7575

7676
## 🚀 Benchmark Results
7777

78+
### Dynamic Graph
79+
80+
| Benchmark Name | Graph Size | Operation | Estimated Time (Median) | Outliers Detected |
81+
|:------------------|:-----------|:-----------|:------------------------|:------------------|
82+
| `small_add_node` | 10 | `add_node` | 29.099 ns | 14% (14 / 100) |
83+
| `medium_add_node` | 100 | `add_node` | 45.864 ns | 12% (12 / 100) |
84+
| `large_add_node` | 1,000 | `add_node` | 39.293 ns | 11% (11 / 100) |
85+
| `small_get_node` | 10 | `get_node` | 3.9417 ns | 8% (8 / 100) |
86+
| `medium_get_node` | 100 | `get_node` | 3.9849 ns | 2% (2 / 100) |
87+
| `large_get_node` | 1,000 | `get_node` | 3.9916 ns | 7% (7 / 100) |
88+
89+
* SMALL = 10;
90+
* MEDIUM = 100;
91+
* LARGE = 1000;
92+
93+
Benchmark source code in [ultragraph/benches ](../ultragraph/benches/benchmarks)
94+
95+
---
96+
97+
* **`add_node` Performance:** Adding a node is consistently fast, with median times ranging from **29 to 46 nanoseconds
98+
**. This confirms it as an efficient O(1) operation. The minor time variations are expected and are due to
99+
system-level memory allocation behavior.
100+
* **`get_node` Performance:** Accessing a node is extremely fast and stable across all graph sizes, with a median time
101+
of approximately **4 nanoseconds**. This demonstrates the O(1) efficiency of retrieving data from the underlying
102+
`Vec`.
103+
* **Outliers:** The presence of outliers is normal for benchmarks running on a non-dedicated machine and indicates that
104+
`Criterion` is correctly identifying and filtering system-level interruptions to provide a more accurate measurement.
105+
106+
### Static CSM Graph
107+
78108
| Operation | Scale | Graph Configuration | Mean Time | Throughput (Est.) |
79109
|:----------------|:------|:---------------------------------------------|:-----------:|:-------------------------|
80110
| **Edge Lookup** | Tiny | `contains_edge` (Linear Scan, degree < 64) | **~7.7 ns** | ~130 Million lookups/sec |
@@ -92,6 +122,8 @@ into a `DynamicGraph`, allowing the cycle of mutation and analysis to begin agai
92122
*(Note: Time units are nanoseconds (ns), microseconds (µs), and milliseconds (ms). Throughput is an approximate
93123
calculation based on the mean time.)*
94124

125+
Benchmark source code in [deep_causality/benches ](../deep_causality/benches)
126+
95127
## Performance Design
96128

97129
The design of `ultragraph`'s static analysis structure, `CsmGraph`, is based on the principles for high-performance

ultragraph/benches/benchmarks/bench_linear.rs

Lines changed: 38 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,24 @@
22
* SPDX-License-Identifier: MIT
33
* Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
44
*/
5-
6-
use criterion::{Criterion, criterion_group};
7-
use rand::Rng;
8-
5+
use criterion::{BatchSize, Criterion, criterion_group};
6+
use rand::{Rng, rng};
7+
use std::hint::black_box;
8+
// Use modern rand imports
99
use ultragraph::*;
1010

1111
use crate::benchmarks::data::Data;
1212
use crate::benchmarks::fields::{LARGE, MEDIUM, SMALL};
1313
use crate::benchmarks::utils;
1414

15-
fn get_empty_ultra_graph(capacity: usize) -> UltraGraph<Data> {
16-
let g: UltraGraphContainer<Data, _> = UltraGraph::with_capacity(capacity, None);
15+
// Type alias for convenience
16+
type UGraph = UltraGraph<Data>;
1717

18-
g
18+
fn get_empty_ultra_graph(capacity: usize) -> UGraph {
19+
UltraGraph::with_capacity(capacity, None)
1920
}
2021

21-
fn get_pre_filled_ultra_graph(capacity: usize) -> UltraGraph<Data> {
22+
fn get_pre_filled_ultra_graph(capacity: usize) -> UGraph {
2223
match capacity {
2324
SMALL => utils::build_linear_graph(SMALL),
2425
MEDIUM => utils::build_linear_graph(MEDIUM),
@@ -27,65 +28,47 @@ fn get_pre_filled_ultra_graph(capacity: usize) -> UltraGraph<Data> {
2728
}
2829
}
2930

30-
fn small_add_node_benchmark(criterion: &mut Criterion) {
31-
let capacity = SMALL;
32-
let mut g = get_empty_ultra_graph(capacity);
33-
criterion.bench_function("small_add_node", |bencher| {
34-
bencher.iter(|| g.add_node(Data::default()))
31+
// Generic benchmark function for adding nodes to avoid repetition
32+
fn bench_add_node(c: &mut Criterion, name: &str, capacity: usize) {
33+
let d = Data::default();
34+
35+
c.bench_function(name, |b| {
36+
// Use iter_batched to create a new graph for each measurement
37+
b.iter_batched(
38+
|| get_empty_ultra_graph(capacity), // SETUP: Create a fresh, empty graph
39+
|mut g| g.add_node(d), // ROUTINE: The operation to benchmark
40+
BatchSize::LargeInput, // A hint for criterion about the workload
41+
);
3542
});
3643
}
3744

38-
fn small_get_node_benchmark(criterion: &mut Criterion) {
39-
let capacity = SMALL;
45+
// Generic benchmark function for getting nodes to avoid repetition
46+
fn bench_get_node(c: &mut Criterion, name: &str, capacity: usize) {
4047
let g = get_pre_filled_ultra_graph(capacity);
48+
let mut rng = rng(); // Create RNG once
4149

42-
criterion.bench_function("small_get_node", |bencher| {
43-
bencher.iter(|| g.get_node(rand::rng().random_range(0..capacity)))
44-
});
45-
}
46-
47-
fn medium_add_node_benchmark(criterion: &mut Criterion) {
48-
let capacity = MEDIUM;
49-
let mut g = get_empty_ultra_graph(capacity);
50-
criterion.bench_function("medium_add_node", |bencher| {
51-
bencher.iter(|| g.add_node(Data::default()))
50+
c.bench_function(name, |b| {
51+
b.iter(|| {
52+
let index = rng.random_range(0..capacity);
53+
black_box(g.get_node(index))
54+
})
5255
});
5356
}
5457

55-
fn medium_get_node_benchmark(criterion: &mut Criterion) {
56-
let capacity = MEDIUM;
57-
let g = get_pre_filled_ultra_graph(capacity);
58+
fn linear_graph_benchmarks(c: &mut Criterion) {
59+
// Benchmarks for adding nodes
60+
bench_add_node(c, "small_add_node", SMALL);
61+
bench_add_node(c, "medium_add_node", MEDIUM);
62+
bench_add_node(c, "large_add_node", LARGE);
5863

59-
criterion.bench_function("medium_get_node", |bencher| {
60-
bencher.iter(|| g.get_node(rand::rng().random_range(0..capacity)))
61-
});
62-
}
63-
64-
fn large_add_node_benchmark(criterion: &mut Criterion) {
65-
let capacity = LARGE;
66-
let mut g = get_empty_ultra_graph(capacity);
67-
criterion.bench_function("large_add_node", |bencher| {
68-
bencher.iter(|| g.add_node(Data::default()))
69-
});
70-
}
71-
72-
fn large_get_node_benchmark(criterion: &mut Criterion) {
73-
let capacity = LARGE;
74-
let g = get_pre_filled_ultra_graph(capacity);
75-
76-
criterion.bench_function("array_push", |bencher| {
77-
bencher.iter(|| g.get_node(rand::rng().random_range(0..capacity)))
78-
});
64+
// Benchmarks for getting nodes
65+
bench_get_node(c, "small_get_node", SMALL);
66+
bench_get_node(c, "medium_get_node", MEDIUM);
67+
bench_get_node(c, "large_get_node", LARGE);
7968
}
8069

8170
criterion_group! {
8271
name = liner_graph_bench_collection;
8372
config = Criterion::default().sample_size(100);
84-
targets =
85-
small_add_node_benchmark,
86-
small_get_node_benchmark,
87-
medium_add_node_benchmark,
88-
medium_get_node_benchmark,
89-
large_add_node_benchmark,
90-
large_get_node_benchmark,
73+
targets = linear_graph_benchmarks
9174
}

ultragraph/benches/benchmarks/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use ultragraph::*;
88
use crate::benchmarks::data::Data;
99

1010
pub fn build_linear_graph(k: usize) -> UltraGraph<Data> {
11-
let mut g: UltraGraphContainer<Data, _> = UltraGraph::new();
11+
let mut g: UltraGraphContainer<Data, _> = UltraGraph::with_capacity(k, None);
1212

1313
let d = Data::default();
1414

0 commit comments

Comments
 (0)