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(64 * 1024, 16 * 1024);
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(64 * 1024, 16 * 1024);
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                        unsafe {
123                            nodes[i]
124                                .with_write_barrier(&mut context, |node| node.neighbors.push(n));
125                        }
126                    }
127                }
128                // Every 10 nodes form a cycle
129                if i % 10 == 0 && i + 9 < size {
130                    let n = nodes[i];
131                    unsafe {
132                        nodes[i + 9]
133                            .with_write_barrier(&mut context, |node| node.neighbors.push(n));
134                    }
135                }
136            }
137        }
138        let graph_duration = graph_start.elapsed();
139
140        println!("  Built complex object graph in: {:?}", graph_duration);
141
142        // Measure GC performance
143        let gc_start = Instant::now();
144        let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
145        let gc_duration = gc_start.elapsed();
146
147        println!("  GC回收 {} 字节耗时: {:?}", freed, gc_duration);
148        println!(
149            "  Object graph complexity: average {} neighbors per node",
150            if size > 0 { (size * 5) / size } else { 0 }
151        );
152    }
153}
154
155/// Test memory usage efficiency
156fn benchmark_memory_efficiency() {
157    println!("\nTesting memory usage efficiency...");
158
159    let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
160    context.set_memory_limit(1024 * 1024); // 1MB global limit
161    let partition = context.create_partition(64 * 1024, 16 * 1024);
162
163    // Allocate many small objects
164    let small_objects_count = 1000;
165    let mut small_objects = Vec::new();
166
167    for _i in 0..small_objects_count {
168        let obj = unsafe { context.alloc_raw(partition, SmallData {}) }.unwrap();
169        small_objects.push(obj);
170    }
171
172    if let Some(partition_info) = context.partition(partition) {
173        let used = partition_info.memory_used();
174        let limit = context.memory_limit();
175        let efficiency = if limit > 0 {
176            (used as f64 / limit as f64) * 100.0
177        } else {
178            0.0
179        };
180
181        println!("  After allocating {} small objects:", small_objects_count);
182        println!(
183            "  Memory usage: {}/{} bytes ({:.1}%)",
184            used, limit, efficiency
185        );
186        println!(
187            "  Average overhead per object: {} bytes",
188            if small_objects_count > 0 {
189                used / small_objects_count
190            } else {
191                0
192            }
193        );
194    }
195
196    // Collect all objects
197    let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
198    println!("  Collected all objects, freed {} bytes", freed);
199
200    // Verify complete memory collection
201    if let Some(partition_info) = context.partition(partition) {
202        let used_after = partition_info.memory_used();
203        println!("  Memory usage after collection: {} bytes", used_after);
204        println!(
205            "  Memory collection rate: {:.1}%",
206            if freed > 0 {
207                (freed as f64 / (freed + used_after) as f64) * 100.0
208            } else {
209                0.0
210            }
211        );
212    }
213}
214
215/// Test automatic GC threshold performance
216fn benchmark_auto_gc_threshold() {
217    println!("\nTesting automatic GC threshold performance...");
218
219    let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
220    context.set_memory_limit(2048); // 2KB global limit
221    let partition = context.create_partition(64 * 1024, 16 * 1024);
222
223    // Set automatic GC threshold to 1.5KB
224    context.set_gc_threshold(1500);
225
226    // Allocate objects until automatic GC is triggered
227    let mut allocated_bytes = 0;
228    let mut object_count = 0;
229
230    println!("  Allocating objects until automatic GC is triggered...");
231
232    for _i in 0..100 {
233        // Try at most 100 times
234        // Allocate objects of about 100 bytes
235        let node = SimpleNode {
236            _data: vec![0u8; 100],
237        };
238        match unsafe { context.alloc_raw(partition, node) } {
239            Ok(_gc_ref) => {
240                allocated_bytes += 100 + std::mem::size_of::<GcRef<SimpleNode>>(); // Estimated size
241                object_count += 1;
242
243                // Check if approaching threshold
244                if let Some(partition_info) = context.partition(partition)
245                    && partition_info.memory_used() >= 1500
246                {
247                    println!(
248                        "  Reached automatic GC threshold, allocated {} objects",
249                        object_count
250                    );
251                    println!("  Estimated allocated memory: {} bytes", allocated_bytes);
252                    println!(
253                        "  Actual memory usage: {} bytes",
254                        partition_info.memory_used()
255                    );
256                    break;
257                }
258            }
259            Err(_) => {
260                println!("  Allocation failed, automatic GC may have been triggered");
261                break;
262            }
263        }
264    }
265
266    // Manually trigger GC to see effect
267    let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
268    println!("  Manual GC freed {} bytes", freed);
269}
270
271// Supporting type definitions
272
273/// Simple node for performance testing
274#[derive(Debug)]
275struct SimpleNode {
276    _data: Vec<u8>,
277}
278
279impl GcTrace for SimpleNode {
280    fn trace(&self, _: &mut GcTraceCtx) {}
281}
282
283/// Graph node for complex object graph testing
284#[derive(Debug)]
285struct GraphNode {
286    neighbors: Vec<GcRef<GraphNode>>,
287}
288
289impl GcTrace for GraphNode {
290    fn trace(&self, tr: &mut GcTraceCtx) {
291        for neighbor in &self.neighbors {
292            tr.add(*neighbor);
293        }
294    }
295}
296
297/// Small data object for memory efficiency testing
298#[derive(Debug)]
299struct SmallData {
300    // To avoid unused field warnings, no id and value fields included here
301}
302
303impl GcTrace for SmallData {
304    fn trace(&self, _: &mut GcTraceCtx) {}
305}
306
307gc_type_register! {
308    SimpleNode;
309    GraphNode;
310    SmallData;
311}