Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
439 changes: 439 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ serde_json = "1"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
criterion = { version = "0.5", features = ["html_reports"] }
7 changes: 7 additions & 0 deletions crates/6g-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,10 @@ sixg-rrc = { path = "../6g-rrc" }
sixg-semantic = { path = "../6g-semantic" }
sixg-ntn = { path = "../6g-ntn" }
serde.workspace = true

[dev-dependencies]
criterion.workspace = true

[[bench]]
name = "core_capacity"
harness = false
97 changes: 97 additions & 0 deletions crates/6g-core/benches/core_capacity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! Capacity benchmarks for the 6G Core Network.
//!
//! Benchmarks the hot-path computations for:
//! - `core_register_burst`: UE registration burst throughput
//!
//! Run with:
//! cargo bench -p sixg-core --bench core_capacity

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use sixg_common::types::UeId;
use sixg_core::{nssf::SliceType, smf::PduSessionType, CoreNetwork};

/// Benchmark UE registration burst throughput.
///
/// Metric: registrations per second.
fn bench_core_register_burst(c: &mut Criterion) {
let mut group = c.benchmark_group("core_register_burst");

for &n_ues in &[10usize, 50, 100, 250, 500, 1000] {
group.throughput(Throughput::Elements(n_ues as u64));
group.bench_with_input(BenchmarkId::new("ues", n_ues), &n_ues, |b, &n| {
b.iter(|| {
let mut core = CoreNetwork::new();
let mut registered = 0usize;
for i in 0..n {
if core.register_ue(black_box(UeId(i as u64)), 1) {
registered += 1;
}
}
black_box(registered)
})
});
}
group.finish();
}

/// Benchmark full session establishment (registration + PDU session setup).
fn bench_session_establishment(c: &mut Criterion) {
let mut group = c.benchmark_group("session_establishment");

for &n_ues in &[10usize, 50, 100] {
group.throughput(Throughput::Elements(n_ues as u64));
group.bench_with_input(BenchmarkId::new("ues", n_ues), &n_ues, |b, &n| {
b.iter(|| {
let mut core = CoreNetwork::new();
let mut sessions = 0usize;
for i in 0..n {
let ue = UeId(i as u64);
if core.register_ue(ue, 1) {
if core
.establish_session(ue, SliceType::EmBb, PduSessionType::Ip)
.is_some()
{
sessions += 1;
}
}
}
black_box(sessions)
})
});
}
group.finish();
}

/// Benchmark UPF unknown-flow buffering (user-plane-first path).
fn bench_upf_unknown_flow(c: &mut Criterion) {
let mut group = c.benchmark_group("upf_unknown_flow");
use sixg_core::upf::Upf;

for &n_ues in &[100usize, 500, 1000] {
group.throughput(Throughput::Elements(n_ues as u64));
group.bench_with_input(BenchmarkId::new("ues", n_ues), &n_ues, |b, &n| {
b.iter(|| {
let mut upf = Upf::new();
let payload = b"first packet payload for lazy establishment test";
let mut actions = 0usize;
for i in 0..n {
use sixg_core::upf::FlowAction;
let action = upf.forward_unknown_flow(UeId(i as u64), black_box(payload));
if action == FlowAction::TriggerEstablishment(UeId(i as u64)) {
actions += 1;
}
}
black_box(actions)
})
});
}
group.finish();
}

criterion_group!(
benches,
bench_core_register_burst,
bench_session_establishment,
bench_upf_unknown_flow
);
criterion_main!(benches);
7 changes: 7 additions & 0 deletions crates/6g-mac/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,10 @@ sixg-phy = { path = "../6g-phy" }
sixg-ai = { path = "../6g-ai" }
thiserror.workspace = true
serde.workspace = true

[dev-dependencies]
criterion.workspace = true

[[bench]]
name = "mac_capacity"
harness = false
133 changes: 133 additions & 0 deletions crates/6g-mac/benches/mac_capacity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! Capacity benchmarks for the 6G MAC layer.
//!
//! Benchmarks the hot-path computations for:
//! - `scheduler_scale`: scheduling throughput at various UE counts and TTI counts
//! - `harq_rounds_distribution`: HARQ Chase Combining across SNR values
//!
//! Run with:
//! cargo bench -p sixg-mac --bench mac_capacity

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use sixg_common::types::{SnrLinear, UeId};
use sixg_mac::{
harq::ChaseCombineBuffer,
scheduler::{jain_fairness, Scheduler, SchedulingPolicy, UeChannelState},
};

/// Benchmark the scheduler across a range of UE counts and TTI depths.
///
/// Metric: TTIs per second (throughput of the scheduling loop).
fn bench_scheduler_scale(c: &mut Criterion) {
let mut group = c.benchmark_group("scheduler_scale");

for &n_ues in &[8usize, 64, 128, 256, 512] {
// Build channel states with varied SNR (linear 1..n_ues).
let states: Vec<UeChannelState> = (0..n_ues)
.map(|i| UeChannelState::new(UeId(i as u64), SnrLinear::new(1.0 + i as f64 * 0.5)))
.collect();

const TOTAL_RBS: usize = 273;
const N_TTI: usize = 100;

group.throughput(Throughput::Elements(N_TTI as u64));

// AI-native (Q-bandit) policy
group.bench_with_input(
BenchmarkId::new("ai_native_ues", n_ues),
&states,
|b, sts| {
b.iter(|| {
let mut sched = Scheduler::with_policy(SchedulingPolicy::AiNative);
let mut total = 0usize;
for _tti in 0..N_TTI {
let assignments = sched.schedule_with_csi(black_box(sts), TOTAL_RBS);
total += assignments.len();
for (idx, a) in assignments.iter().enumerate() {
sched.observe_reward(
idx,
black_box(sts[idx % sts.len()].snr),
a.rb_count as f64 * 1e6,
);
}
}
black_box(total)
})
},
);

// Round-Robin policy (baseline comparison)
group.bench_with_input(
BenchmarkId::new("round_robin_ues", n_ues),
&states,
|b, sts| {
b.iter(|| {
let mut sched = Scheduler::with_policy(SchedulingPolicy::RoundRobin);
let mut total = 0usize;
for _tti in 0..N_TTI {
let a = sched.schedule_with_csi(black_box(sts), TOTAL_RBS);
total += a.len();
}
black_box(total)
})
},
);
}
group.finish();
}

/// Benchmark HARQ Chase Combining across SNR values.
///
/// Metric: HARQ combining rounds needed to reach the decode threshold
/// (measures the distribution shape, not just the scalar output).
fn bench_harq_rounds_distribution(c: &mut Criterion) {
let mut group = c.benchmark_group("harq_rounds_distribution");

// SNR linear values from −5 dB to 20 dB
let snr_values: Vec<f64> = (-5..=20).map(|db| 10f64.powf(db as f64 / 10.0)).collect();

const N_SAMPLES: usize = 10_000;

group.throughput(Throughput::Elements(N_SAMPLES as u64));
group.bench_function("chase_combining_10k_samples", |b| {
b.iter(|| {
let mut rounds_total = 0u64;
for (i, &snr) in snr_values
.iter()
.enumerate()
.take(N_SAMPLES % snr_values.len() + 1)
{
let mut buf = ChaseCombineBuffer::default();
let mut rounds = 0u8;
while !buf.can_decode() && rounds < 8 {
buf.combine(black_box(snr * (1.0 + 0.01 * i as f64)));
rounds += 1;
}
rounds_total += rounds as u64;
}
black_box(rounds_total)
})
});

group.finish();
}

/// Benchmark Jain fairness computation at various UE counts.
fn bench_jain_fairness(c: &mut Criterion) {
let mut group = c.benchmark_group("jain_fairness");

for &n_ues in &[8usize, 64, 256, 512, 1024] {
let throughputs: Vec<f64> = (1..=n_ues).map(|i| i as f64 * 1e6).collect();
group.bench_with_input(BenchmarkId::new("ues", n_ues), &throughputs, |b, tp| {
b.iter(|| black_box(jain_fairness(black_box(tp))))
});
}
group.finish();
}

criterion_group!(
benches,
bench_scheduler_scale,
bench_harq_rounds_distribution,
bench_jain_fairness
);
criterion_main!(benches);
7 changes: 7 additions & 0 deletions crates/6g-ntn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ license.workspace = true
sixg-common = { path = "../6g-common" }
thiserror.workspace = true
serde.workspace = true

[dev-dependencies]
criterion.workspace = true

[[bench]]
name = "ntn_capacity"
harness = false
103 changes: 103 additions & 0 deletions crates/6g-ntn/benches/ntn_capacity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//! Capacity benchmarks for the 6G NTN (Non-Terrestrial Networks) layer.
//!
//! Benchmarks the hot-path computations for:
//! - `ntn_handover_latency`: handover evaluation across altitude tiers
//! - `propagation_delay_sweep`: delay computation across the full altitude range
//!
//! Run with:
//! cargo bench -p sixg-ntn --bench ntn_capacity

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use sixg_common::types::Position3D;
use sixg_common::types::{Distance, PowerDb, UeId};
use sixg_ntn::{
handover::{leo_propagation_delay_ms, HandoverDecision, HandoverTrigger, NtnHandoverManager},
NtnLayer, NtnNode,
};

/// Benchmark handover evaluation across altitude tiers (LEO / HAPS / GEO).
///
/// Metric: handover decisions per second.
fn bench_ntn_handover_latency(c: &mut Criterion) {
let mut group = c.benchmark_group("ntn_handover_latency");
let mgr = NtnHandoverManager::new();

// Altitude tiers: LEO 550 km, HAPS 20 km, MEO 8000 km, GEO 35786 km
let altitudes: &[(&str, f64)] = &[
("leo_550km", 550_000.0),
("haps_20km", 20_000.0),
("meo_8000km", 8_000_000.0),
("geo_35786km", 35_786_000.0),
];

for &(label, alt_m) in altitudes {
let delay_ms = leo_propagation_delay_ms(Distance::from_m(alt_m));
let triggers = vec![HandoverTrigger::PropagationDelayExceeded { delay_ms }];

group.bench_with_input(
BenchmarkId::new("altitude", label),
&triggers,
|b, trigs| {
b.iter(|| {
let decision = mgr.evaluate(black_box(UeId(1)), black_box(trigs));
black_box(decision)
})
},
);
}
group.finish();
}

/// Benchmark propagation delay computation across the full altitude range.
fn bench_propagation_delay_sweep(c: &mut Criterion) {
let mut group = c.benchmark_group("propagation_delay_sweep");

// Sweep from 500 km to 40 000 km in 1000 steps
let altitudes: Vec<Distance> = (0..1000)
.map(|i| Distance::from_m(500_000.0 + i as f64 * 39_500.0))
.collect();

group.throughput(Throughput::Elements(altitudes.len() as u64));
group.bench_function("delay_sweep_1000_altitudes", |b| {
b.iter(|| {
let mut acc = 0.0f64;
for &alt in &altitudes {
acc += leo_propagation_delay_ms(black_box(alt));
}
black_box(acc)
})
});

group.finish();
}

/// Benchmark NTN layer node management at scale.
fn bench_ntn_node_fleet(c: &mut Criterion) {
let mut group = c.benchmark_group("ntn_node_fleet");

for &n_nodes in &[10usize, 100, 500, 1000] {
group.throughput(Throughput::Elements(n_nodes as u64));
group.bench_with_input(BenchmarkId::new("nodes", n_nodes), &n_nodes, |b, &n| {
b.iter(|| {
let mut layer = NtnLayer::new();
for i in 0..n {
let alt = 550_000.0 + (i as f64 * 10.0);
layer.add_node(NtnNode::leo_satellite(
i as u64,
Position3D::new(0.0, i as f64 * 100.0, alt),
));
}
black_box(layer.node_count())
})
});
}
group.finish();
}

criterion_group!(
benches,
bench_ntn_handover_latency,
bench_propagation_delay_sweep,
bench_ntn_node_fleet
);
criterion_main!(benches);
5 changes: 5 additions & 0 deletions crates/6g-phy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ serde.workspace = true
baseline-comparison = []

[dev-dependencies]
criterion.workspace = true

[[bench]]
name = "phy_capacity"
harness = false
Loading
Loading