use safer_ring::perf::{MemoryTracker, PerfCounter, PerfRegistry, PerfTimer};
use safer_ring::{BufferPool, PinnedBuffer, Ring};
use std::io::Write;
use std::os::unix::io::AsRawFd;
use std::time::{Duration, Instant};
use tempfile::NamedTempFile;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Safer-Ring Performance Optimization Demo");
println!("=======================================");
let test_file = create_test_file(1024 * 1024)?; let fd = test_file.as_raw_fd();
let mut perf_registry = PerfRegistry::new();
let read_counter = perf_registry.counter("read_operations");
let _write_counter = perf_registry.counter("write_operations");
let memory_tracker = perf_registry.memory_tracker();
println!("\n1. Basic Buffer Operations");
demo_basic_buffers(&read_counter, &memory_tracker).await?;
println!("\n2. Buffer Pool Optimization");
demo_buffer_pool(&read_counter, fd).await?;
println!("\n3. NUMA-Aware Allocation");
demo_numa_allocation(&memory_tracker).await?;
println!("\n4. Batch Operations");
demo_batch_operations(&read_counter, fd).await?;
println!("\n5. Performance Monitoring");
demo_performance_monitoring(&mut perf_registry).await?;
println!("\n6. Memory Optimization");
demo_memory_optimization(&memory_tracker).await?;
print_performance_summary(&perf_registry);
Ok(())
}
fn create_test_file(size: usize) -> Result<NamedTempFile, Box<dyn std::error::Error>> {
let mut file = NamedTempFile::new()?;
let data = vec![0u8; size];
std::io::Write::write_all(&mut file, &data)?;
file.flush()?;
Ok(file)
}
async fn demo_basic_buffers(
_counter: &PerfCounter,
memory_tracker: &MemoryTracker,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Testing different buffer allocation strategies...");
let initial_memory = memory_tracker.current_usage();
let start = Instant::now();
let _buffer1 = PinnedBuffer::with_capacity(4096);
let standard_time = start.elapsed();
let start = Instant::now();
let _buffer2 = PinnedBuffer::with_capacity_aligned(4096);
let aligned_time = start.elapsed();
let start = Instant::now();
let _buffer3 = PinnedBuffer::with_capacity_numa(4096, Some(0));
let numa_time = start.elapsed();
memory_tracker.record_alloc(4096 * 3);
println!(" Standard allocation: {standard_time:?}");
println!(" Aligned allocation: {aligned_time:?}");
println!(" NUMA allocation: {numa_time:?}");
println!(
" Memory used: {} bytes",
memory_tracker.current_usage() - initial_memory
);
Ok(())
}
async fn demo_buffer_pool(
_counter: &PerfCounter,
_fd: i32,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Comparing direct allocation vs buffer pool...");
let start = Instant::now();
for _i in 0..50 {
let _buffer = PinnedBuffer::with_capacity(4096);
tokio::time::sleep(Duration::from_micros(10)).await;
}
let direct_time = start.elapsed();
let start = Instant::now();
let pool = BufferPool::new(100, 4096);
for _i in 0..50 {
if let Some(_buffer) = pool.get() {
tokio::time::sleep(Duration::from_micros(10)).await;
}
}
let pool_time = start.elapsed();
println!(
" Direct allocation: {:?} ({:.2} ops/sec)",
direct_time,
50.0 / direct_time.as_secs_f64()
);
println!(
" Buffer pool: {:?} ({:.2} ops/sec)",
pool_time,
50.0 / pool_time.as_secs_f64()
);
let speedup = direct_time.as_secs_f64() / pool_time.as_secs_f64();
println!(" Speedup: {speedup:.2}x");
Ok(())
}
async fn demo_numa_allocation(
memory_tracker: &MemoryTracker,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Testing NUMA-aware buffer allocation...");
let initial_memory = memory_tracker.current_usage();
let mut buffers = Vec::new();
for node in 0..2 {
let start = Instant::now();
for _ in 0..10 {
let buffer = PinnedBuffer::with_capacity_numa(4096, Some(node));
buffers.push(buffer);
}
let allocation_time = start.elapsed();
println!(" NUMA node {node}: {allocation_time:?} for 10 buffers");
}
memory_tracker.record_alloc(4096 * 20);
println!(
" Total memory allocated: {} bytes",
memory_tracker.current_usage() - initial_memory
);
Ok(())
}
async fn demo_batch_operations(
_counter: &PerfCounter,
_fd: i32,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Demonstrating batch vs individual operations...");
let pool = BufferPool::new(100, 4096);
let start = Instant::now();
for _i in 0..20 {
let _ring = Ring::new(32)?;
if let Some(_buffer) = pool.get() {
tokio::time::sleep(Duration::from_micros(50)).await;
}
}
let individual_time = start.elapsed();
let start = Instant::now();
let _ring = Ring::new(64)?;
for _i in 0..20 {
if let Some(_buffer) = pool.get() {
tokio::time::sleep(Duration::from_micros(30)).await;
}
}
let batch_time = start.elapsed();
println!(" Individual setup: {individual_time:?}");
println!(" Batch operations: {batch_time:?}");
let speedup = individual_time.as_secs_f64() / batch_time.as_secs_f64();
println!(" Speedup: {speedup:.2}x");
Ok(())
}
async fn demo_performance_monitoring(
registry: &mut PerfRegistry,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Demonstrating performance monitoring...");
let operation_counter = registry.counter("demo_operations");
let memory_tracker = registry.memory_tracker();
for i in 0..100 {
let _timer = PerfTimer::new(&operation_counter);
tokio::time::sleep(Duration::from_micros(10 + i % 50)).await;
if i % 10 == 0 {
memory_tracker.record_alloc(1024);
}
}
let stats = operation_counter.stats();
println!(" Operations completed: {}", stats.count);
println!(" Average time: {:?}", stats.avg_time);
println!(" Min time: {:?}", stats.min_time);
println!(" Max time: {:?}", stats.max_time);
println!(" Ops/sec: {:.2}", stats.ops_per_sec());
let memory_stats = memory_tracker.stats();
println!(" Memory allocations: {}", memory_stats.total_allocs);
println!(" Current usage: {} bytes", memory_stats.current);
println!(" Peak usage: {} bytes", memory_stats.peak);
Ok(())
}
async fn demo_memory_optimization(
memory_tracker: &MemoryTracker,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Testing memory optimization strategies...");
let _initial_usage = memory_tracker.current_usage();
let sizes = [1024, 4096, 16384, 65536];
for &size in &sizes {
let start_memory = memory_tracker.current_usage();
let mut buffers = Vec::new();
for _ in 0..10 {
buffers.push(PinnedBuffer::with_capacity_aligned(size));
}
memory_tracker.record_alloc(size * 10);
let end_memory = memory_tracker.current_usage();
println!(
" {} byte buffers: {} bytes allocated",
size,
end_memory - start_memory
);
}
let pool_start = memory_tracker.current_usage();
let pool = BufferPool::new(50, 4096);
memory_tracker.record_alloc(4096 * 50);
let pool_end = memory_tracker.current_usage();
println!(" Buffer pool (50x4KB): {} bytes", pool_end - pool_start);
let mut acquired_buffers = Vec::new();
for _ in 0..25 {
if let Some(buffer) = pool.get() {
acquired_buffers.push(buffer);
}
}
let pool_stats = pool.stats();
println!(
" Pool utilization: {:.1}%",
pool_stats.utilization * 100.0
);
println!(" Available buffers: {}", pool_stats.available);
Ok(())
}
fn print_performance_summary(registry: &PerfRegistry) {
println!("\nPerformance Summary");
println!("==================");
let all_stats = registry.all_stats();
for (name, stats) in all_stats {
println!("{name}:");
println!(" Operations: {}", stats.count);
println!(" Avg time: {:?}", stats.avg_time);
println!(" Throughput: {:.2} ops/sec", stats.ops_per_sec());
}
let memory_stats = registry.memory_tracker().stats();
println!("\nMemory Usage:");
println!(" Current: {} bytes", memory_stats.current);
println!(" Peak: {} bytes", memory_stats.peak);
println!(" Total allocations: {}", memory_stats.total_allocs);
println!(" Total deallocations: {}", memory_stats.total_deallocs);
println!("\nOptimization Recommendations:");
if memory_stats.peak > 10 * 1024 * 1024 {
println!(" - Consider using smaller buffer pools to reduce memory usage");
}
if memory_stats.total_allocs > memory_stats.total_deallocs * 2 {
println!(" - High allocation rate detected, consider buffer reuse");
}
for (name, stats) in registry.all_stats() {
if stats.ops_per_sec() < 1000.0 && stats.count > 10 {
println!(" - {name} has low throughput, consider optimization");
}
}
println!("\nFor detailed profiling, run: ./scripts/run_benchmarks.sh");
}