use gc_lite::{GcHeap, GcRef, GcTrace, GcTraceCtx, gc_type_register};
use std::time::{Duration, Instant};
fn main() {
println!("=== Performance benchmark of partitioned garbage collection system ===");
println!("\n=== Object size performance test ===");
benchmark_object_sizes();
println!("\n=== Complex object graph performance test ===");
benchmark_complex_graphs();
println!("\n=== Memory usage efficiency test ===");
benchmark_memory_efficiency();
println!("\n=== Automatic GC threshold performance test ===");
benchmark_auto_gc_threshold();
println!("\nAll performance tests completed!");
}
fn benchmark_object_sizes() {
let sizes = [10, 100, 500, 1000, 5000];
for &size in &sizes {
println!("\nTest object count: {}", size);
let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
let partition = context.create_partition(64 * 1024, 16 * 1024);
let alloc_start = Instant::now();
let mut objects = Vec::new();
for _i in 0..size {
let node = unsafe {
context.alloc_raw(
partition,
SimpleNode {
_data: vec![0u8; 100],
},
)
} .unwrap();
objects.push(node);
}
let alloc_duration = alloc_start.elapsed();
println!(" Allocated {} objects in: {:?}", size, alloc_duration);
println!(
" Average allocation time per object: {:?}",
alloc_duration / size as u32
);
let gc_start = Instant::now();
let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
let gc_duration = gc_start.elapsed();
println!(" GC回收 {} 字节耗时: {:?}", freed, gc_duration);
println!(
" Average collection time per byte: {:?}",
if freed > 0 {
gc_duration / freed as u32
} else {
Duration::from_nanos(0)
}
);
}
}
fn benchmark_complex_graphs() {
let sizes = [100, 500, 1000];
for &size in &sizes {
println!("\nTest complex object graph size: {} nodes", size);
let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
let partition = context.create_partition(64 * 1024, 16 * 1024);
let graph_start = Instant::now();
let mut nodes = Vec::new();
for _i in 0..size {
let node = unsafe {
context.alloc_raw(
partition,
GraphNode {
neighbors: Vec::new(),
},
)
}
.unwrap();
nodes.push(node);
}
for i in 0..size {
{
for j in 1..=5 {
if i + j < size {
let n = nodes[i + j];
unsafe {
nodes[i]
.with_write_barrier(&mut context, |node| node.neighbors.push(n));
}
}
}
if i % 10 == 0 && i + 9 < size {
let n = nodes[i];
unsafe {
nodes[i + 9]
.with_write_barrier(&mut context, |node| node.neighbors.push(n));
}
}
}
}
let graph_duration = graph_start.elapsed();
println!(" Built complex object graph in: {:?}", graph_duration);
let gc_start = Instant::now();
let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
let gc_duration = gc_start.elapsed();
println!(" GC回收 {} 字节耗时: {:?}", freed, gc_duration);
println!(
" Object graph complexity: average {} neighbors per node",
if size > 0 { (size * 5) / size } else { 0 }
);
}
}
fn benchmark_memory_efficiency() {
println!("\nTesting memory usage efficiency...");
let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
context.set_memory_limit(1024 * 1024); let partition = context.create_partition(64 * 1024, 16 * 1024);
let small_objects_count = 1000;
let mut small_objects = Vec::new();
for _i in 0..small_objects_count {
let obj = unsafe { context.alloc_raw(partition, SmallData {}) }.unwrap();
small_objects.push(obj);
}
if let Some(partition_info) = context.partition(partition) {
let used = partition_info.memory_used();
let limit = context.memory_limit();
let efficiency = if limit > 0 {
(used as f64 / limit as f64) * 100.0
} else {
0.0
};
println!(" After allocating {} small objects:", small_objects_count);
println!(
" Memory usage: {}/{} bytes ({:.1}%)",
used, limit, efficiency
);
println!(
" Average overhead per object: {} bytes",
if small_objects_count > 0 {
used / small_objects_count
} else {
0
}
);
}
let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
println!(" Collected all objects, freed {} bytes", freed);
if let Some(partition_info) = context.partition(partition) {
let used_after = partition_info.memory_used();
println!(" Memory usage after collection: {} bytes", used_after);
println!(
" Memory collection rate: {:.1}%",
if freed > 0 {
(freed as f64 / (freed + used_after) as f64) * 100.0
} else {
0.0
}
);
}
}
fn benchmark_auto_gc_threshold() {
println!("\nTesting automatic GC threshold performance...");
let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
context.set_memory_limit(2048); let partition = context.create_partition(64 * 1024, 16 * 1024);
context.set_gc_threshold(1500);
let mut allocated_bytes = 0;
let mut object_count = 0;
println!(" Allocating objects until automatic GC is triggered...");
for _i in 0..100 {
let node = SimpleNode {
_data: vec![0u8; 100],
};
match unsafe { context.alloc_raw(partition, node) } {
Ok(_gc_ref) => {
allocated_bytes += 100 + std::mem::size_of::<GcRef<SimpleNode>>(); object_count += 1;
if let Some(partition_info) = context.partition(partition)
&& partition_info.memory_used() >= 1500
{
println!(
" Reached automatic GC threshold, allocated {} objects",
object_count
);
println!(" Estimated allocated memory: {} bytes", allocated_bytes);
println!(
" Actual memory usage: {} bytes",
partition_info.memory_used()
);
break;
}
}
Err(_) => {
println!(" Allocation failed, automatic GC may have been triggered");
break;
}
}
}
let freed = context.garbage_collect(partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
println!(" Manual GC freed {} bytes", freed);
}
#[derive(Debug)]
struct SimpleNode {
_data: Vec<u8>,
}
impl GcTrace for SimpleNode {
fn trace(&self, _: &mut GcTraceCtx) {}
}
#[derive(Debug)]
struct GraphNode {
neighbors: Vec<GcRef<GraphNode>>,
}
impl GcTrace for GraphNode {
fn trace(&self, tr: &mut GcTraceCtx) {
for neighbor in &self.neighbors {
tr.add(*neighbor);
}
}
}
#[derive(Debug)]
struct SmallData {
}
impl GcTrace for SmallData {
fn trace(&self, _: &mut GcTraceCtx) {}
}
gc_type_register! {
SimpleNode;
GraphNode;
SmallData;
}