Skip to main content

performance_benchmark/
performance_benchmark.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright (c) 2025-2026 John Ray <996351336@qq.com>
3
4//! Performance benchmark example
5//!
6//! Demonstrates performance characteristics of the partitioned garbage collection system, including:
7//! - Allocation and collection performance for objects of different sizes
8//! - GC performance for complex object graphs
9//! - Memory usage efficiency analysis
10//! - Automatic GC threshold performance
11
12use gc_lite::{GcHeap, GcRef, GcTrace, GcTraceCtx, gc_type_register};
13use std::time::{Duration, Instant};
14
15fn main() {
16    println!("=== Performance benchmark of partitioned garbage collection system ===");
17
18    // Test allocation and collection of objects of different sizes
19    println!("\n=== Object size performance test ===");
20    benchmark_object_sizes();
21
22    // Test GC performance of complex object graphs
23    println!("\n=== Complex object graph performance test ===");
24    benchmark_complex_graphs();
25
26    // Test memory usage efficiency
27    println!("\n=== Memory usage efficiency test ===");
28    benchmark_memory_efficiency();
29
30    // Test automatic GC threshold performance
31    println!("\n=== Automatic GC threshold performance test ===");
32    benchmark_auto_gc_threshold();
33
34    println!("\nAll performance tests completed!");
35}
36
37/// Test allocation and collection performance for objects of different sizes
38fn benchmark_object_sizes() {
39    let sizes = [10, 100, 500, 1000, 5000];
40
41    for &size in &sizes {
42        println!("\nTest object count: {}", size);
43
44        let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
45        let partition = context.create_partition();
46
47        // Measure allocation performance
48        let alloc_start = Instant::now();
49        let mut objects = Vec::new();
50        for _i in 0..size {
51            let node = unsafe {
52                context.alloc_raw(
53                    partition,
54                    SimpleNode {
55                        _data: vec![0u8; 100],
56                    },
57                )
58            } // Each node 100 bytes
59            .unwrap();
60            objects.push(node);
61        }
62        let alloc_duration = alloc_start.elapsed();
63
64        println!("  Allocated {} objects in: {:?}", size, alloc_duration);
65        println!(
66            "  Average allocation time per object: {:?}",
67            alloc_duration / size as u32
68        );
69
70        // Measure GC performance (all objects can be collected)
71        let gc_start = Instant::now();
72        let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
73        let gc_duration = gc_start.elapsed();
74
75        println!("  GC回收 {} 字节耗时: {:?}", freed, gc_duration);
76        println!(
77            "  Average collection time per byte: {:?}",
78            if freed > 0 {
79                gc_duration / freed as u32
80            } else {
81                Duration::from_nanos(0)
82            }
83        );
84    }
85}
86
87/// Test GC performance of complex object graphs
88fn benchmark_complex_graphs() {
89    let sizes = [100, 500, 1000];
90
91    for &size in &sizes {
92        println!("\nTest complex object graph size: {} nodes", size);
93
94        let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
95        let partition = context.create_partition();
96
97        // Create complex object graph
98        let graph_start = Instant::now();
99        let mut nodes = Vec::new();
100
101        // Create all nodes
102        for _i in 0..size {
103            let node = unsafe {
104                context.alloc_raw(
105                    partition,
106                    GraphNode {
107                        neighbors: Vec::new(),
108                    },
109                )
110            }
111            .unwrap();
112            nodes.push(node);
113        }
114
115        // Establish complex dependencies
116        for i in 0..size {
117            {
118                // Each node points to subsequent nodes
119                for j in 1..=5 {
120                    if i + j < size {
121                        let n = nodes[i + j];
122                        nodes[i].with_mut(&mut context, |node| node.neighbors.push(n));
123                    }
124                }
125                // Every 10 nodes form a cycle
126                if i % 10 == 0 && i + 9 < size {
127                    let n = nodes[i];
128                    nodes[i + 9].with_mut(&mut context, |node| node.neighbors.push(n));
129                }
130            }
131        }
132        let graph_duration = graph_start.elapsed();
133
134        println!("  Built complex object graph in: {:?}", graph_duration);
135
136        // Measure GC performance
137        let gc_start = Instant::now();
138        let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
139        let gc_duration = gc_start.elapsed();
140
141        println!("  GC回收 {} 字节耗时: {:?}", freed, gc_duration);
142        println!(
143            "  Object graph complexity: average {} neighbors per node",
144            if size > 0 { (size * 5) / size } else { 0 }
145        );
146    }
147}
148
149/// Test memory usage efficiency
150fn benchmark_memory_efficiency() {
151    println!("\nTesting memory usage efficiency...");
152
153    let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
154    context.set_memory_limit(1024 * 1024); // 1MB global limit
155    let partition = context.create_partition();
156
157    // Allocate many small objects
158    let small_objects_count = 1000;
159    let mut small_objects = Vec::new();
160
161    for _i in 0..small_objects_count {
162        let obj = unsafe { context.alloc_raw(partition, SmallData {}) }.unwrap();
163        small_objects.push(obj);
164    }
165
166    if let Some(partition_info) = context.partition(partition) {
167        let used = partition_info.memory_used();
168        let limit = context.memory_limit();
169        let efficiency = if limit > 0 {
170            (used as f64 / limit as f64) * 100.0
171        } else {
172            0.0
173        };
174
175        println!("  After allocating {} small objects:", small_objects_count);
176        println!(
177            "  Memory usage: {}/{} bytes ({:.1}%)",
178            used, limit, efficiency
179        );
180        println!(
181            "  Average overhead per object: {} bytes",
182            if small_objects_count > 0 {
183                used / small_objects_count
184            } else {
185                0
186            }
187        );
188    }
189
190    // Collect all objects
191    let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
192    println!("  Collected all objects, freed {} bytes", freed);
193
194    // Verify complete memory collection
195    if let Some(partition_info) = context.partition(partition) {
196        let used_after = partition_info.memory_used();
197        println!("  Memory usage after collection: {} bytes", used_after);
198        println!(
199            "  Memory collection rate: {:.1}%",
200            if freed > 0 {
201                (freed as f64 / (freed + used_after) as f64) * 100.0
202            } else {
203                0.0
204            }
205        );
206    }
207}
208
209/// Test automatic GC threshold performance
210fn benchmark_auto_gc_threshold() {
211    println!("\nTesting automatic GC threshold performance...");
212
213    let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
214    context.set_memory_limit(2048); // 2KB global limit
215    let partition = context.create_partition();
216
217    // Set automatic GC threshold to 1.5KB
218    context.set_gc_threshold(1500);
219
220    // Allocate objects until automatic GC is triggered
221    let mut allocated_bytes = 0;
222    let mut object_count = 0;
223
224    println!("  Allocating objects until automatic GC is triggered...");
225
226    for _i in 0..100 {
227        // Try at most 100 times
228        // Allocate objects of about 100 bytes
229        let node = SimpleNode {
230            _data: vec![0u8; 100],
231        };
232        match unsafe { context.alloc_raw(partition, node) } {
233            Ok(_gc_ref) => {
234                allocated_bytes += 100 + std::mem::size_of::<GcRef<SimpleNode>>(); // Estimated size
235                object_count += 1;
236
237                // Check if approaching threshold
238                if let Some(partition_info) = context.partition(partition)
239                    && partition_info.memory_used() >= 1500
240                {
241                    println!(
242                        "  Reached automatic GC threshold, allocated {} objects",
243                        object_count
244                    );
245                    println!("  Estimated allocated memory: {} bytes", allocated_bytes);
246                    println!(
247                        "  Actual memory usage: {} bytes",
248                        partition_info.memory_used()
249                    );
250                    break;
251                }
252            }
253            Err(_) => {
254                println!("  Allocation failed, automatic GC may have been triggered");
255                break;
256            }
257        }
258    }
259
260    // Manually trigger GC to see effect
261    let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
262    println!("  Manual GC freed {} bytes", freed);
263}
264
265// Supporting type definitions
266
267/// Simple node for performance testing
268#[derive(Debug)]
269struct SimpleNode {
270    _data: Vec<u8>,
271}
272
273impl GcTrace for SimpleNode {
274    fn trace(&self, _: &mut GcTraceCtx) {}
275}
276
277/// Graph node for complex object graph testing
278#[derive(Debug)]
279struct GraphNode {
280    neighbors: Vec<GcRef<GraphNode>>,
281}
282
283impl GcTrace for GraphNode {
284    fn trace(&self, tr: &mut GcTraceCtx) {
285        for neighbor in &self.neighbors {
286            tr.add(*neighbor);
287        }
288    }
289}
290
291/// Small data object for memory efficiency testing
292#[derive(Debug)]
293struct SmallData {
294    // To avoid unused field warnings, no id and value fields included here
295}
296
297impl GcTrace for SmallData {
298    fn trace(&self, _: &mut GcTraceCtx) {}
299}
300
301gc_type_register! {
302    SimpleNode;
303    GraphNode;
304    SmallData;
305}