ICN Profiling Guide
This guide covers how to profile ICN components and analyze performance.
Prerequisites
Install profiling tools:
# Criterion (benchmarking)
cargo install cargo-criterion
# Flamegraph
cargo install flamegraph
# perf (Linux)
sudo apt install linux-tools-common linux-tools-$(uname -r)
# Instruments (macOS)
# Included with Xcode
Running Benchmarks
Basic Benchmark Run
cd icn
cargo bench --workspace
Specific Crate
cargo bench -p icn-trust
cargo bench -p icn-ledger
cargo bench -p icn-gossip
Specific Benchmark
cargo bench --bench trust_bench -- "trust_computation"
cargo bench --bench ledger_bench -- "ed25519"
Baseline Comparison
# Save baseline
cargo bench --workspace -- --save-baseline main
# Compare against baseline
cargo bench --workspace -- --baseline main
Interpreting Results
Criterion Output
trust_computation/10_nodes
time: [43.8 ns 44.2 ns 44.6 ns]
change: [-1.2% +0.5% +2.1%] (p = 0.42 > 0.05)
No change in performance detected.
- time: [lower bound, estimate, upper bound] at 95% confidence
- change: Comparison to previous run (if available)
- p-value: Statistical significance (< 0.05 indicates real change)
What to Look For
| Result | Meaning | Action |
|---|---|---|
No change |
Within noise | No action needed |
Performance has improved |
Genuine speedup | Document improvement |
Performance has regressed |
Genuine slowdown | Investigate cause |
CPU Profiling
Flamegraph (Linux)
# Enable frame pointers for better traces
export RUSTFLAGS="-C force-frame-pointers=yes"
# Profile a specific benchmark
cargo flamegraph --bench trust_bench -- --bench "trust_computation"
# Profile the daemon
cargo flamegraph --bin icnd -- --config /path/to/config.toml
Output: flamegraph.svg - open in browser.
perf (Linux)
# Record profile
perf record -g cargo bench --bench ledger_bench
# Analyze
perf report
# Top functions
perf top -p $(pgrep icnd)
Instruments (macOS)
# Build with debug symbols
cargo build --release
# Run with Instruments
xcrun xctrace record --template 'Time Profiler' \
--launch ./target/release/icnd -- --config config.toml
Memory Profiling
Heap Profiling with DHAT
# Add to Cargo.toml
[profile.bench]
debug = true
# Run with DHAT (requires nightly)
cargo +nightly run --features dhat-heap --bin icnd
Valgrind (Linux)
# Memory usage
valgrind --tool=massif ./target/release/icnd --config config.toml
# Visualize
ms_print massif.out.*
# Memory leaks
valgrind --leak-check=full ./target/release/icnd --config config.toml
heaptrack (Linux)
heaptrack ./target/release/icnd --config config.toml
heaptrack_gui heaptrack.icnd.*.gz
Async Profiling
tokio-console
For debugging async runtime issues:
# Add dependency
cargo add --dev console-subscriber
# In main.rs (dev builds only)
#[cfg(feature = "tokio-console")]
console_subscriber::init();
# Run with tokio-console
RUSTFLAGS="--cfg tokio_unstable" cargo run --features tokio-console
# In another terminal
tokio-console
Tracing
ICN uses tracing for observability. Enable detailed spans:
RUST_LOG=icn_trust=trace cargo run --bin icnd
Network Profiling
Message Timing
Add timing spans to track message latency:
#[tracing::instrument(skip(self))]
async fn handle_message(&self, msg: Message) {
let start = std::time::Instant::now();
// ... process message
tracing::info!(elapsed_ms = ?start.elapsed().as_millis(), "message processed");
}
Bandwidth Analysis
Monitor network traffic:
# Linux: iftop
sudo iftop -i eth0 -f "port 7777"
# Or nethogs for per-process
sudo nethogs
Optimization Workflow
1. Identify Bottleneck
# Run benchmarks
cargo bench --workspace
# Find slowest operations
grep -r "time:" target/criterion/*/new/estimates.json | \
sort -t: -k4 -n | tail -20
2. Profile the Bottleneck
# CPU profile
cargo flamegraph --bench <bench_name> -- --bench "<slow_test>"
3. Analyze Profile
Look for:
- Unexpected function calls
- Deep call stacks
- Hot loops
- Memory allocations in hot paths
4. Implement Fix
Common optimizations:
- Reduce allocations (use
&strinstead ofString) - Cache expensive computations
- Use
SmallVecfor small collections - Batch I/O operations
- Use
rayonfor parallel iteration
5. Verify Improvement
# Compare against baseline
cargo bench -- --baseline before_change
Benchmark Best Practices
Writing Good Benchmarks
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn benchmark_example(c: &mut Criterion) {
// Setup OUTSIDE the benchmark loop
let data = setup_test_data();
c.bench_function("operation_name", |b| {
b.iter(|| {
// Use black_box to prevent optimization
black_box(expensive_operation(&data))
})
});
}
Avoiding Common Pitfalls
- Don't benchmark setup: Move initialization outside
iter() - Use black_box: Prevents dead code elimination
- Warm up caches: Criterion handles this, but be aware
- Isolate tests: Each benchmark should be independent
- Run multiple times: Use CI to catch variance
CI Integration
GitHub Actions Benchmark Job
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run benchmarks
run: cargo bench --workspace -- --save-baseline pr
- name: Compare to main
run: |
git fetch origin main
git checkout origin/main -- target/criterion
cargo bench --workspace -- --baseline main
Regression Thresholds
| Threshold | Action |
|---|---|
| < 10% regression | Pass with note |
| 10-25% regression | Warning, review required |
| > 25% regression | CI failure |