///|
/// Offline switched-fabric queue/capacity/load model for L3F.
///|
pub(all) enum SwitchedFabricQueueDiscipline {
FifoQueue
StrictPriorityQueue
} derive(Eq, Debug)
///|
pub fn SwitchedFabricQueueDiscipline::label(
self : SwitchedFabricQueueDiscipline,
) -> String {
match self {
FifoQueue => "fifo"
StrictPriorityQueue => "strict-priority"
}
}
///|
pub(all) struct SwitchedFabricPort {
port_id : @core.PortId
endpoint : @core.EndpointId
name : String
capacity_octets_per_tick : Int
queue_limit_octets : Int
base_delay : @core.Duration
} derive(Eq, Debug)
///|
pub fn SwitchedFabricPort::new(
port_id~ : @core.PortId,
endpoint~ : @core.EndpointId,
name~ : String,
capacity_octets_per_tick~ : Int,
queue_limit_octets~ : Int,
base_delay~ : @core.Duration,
) -> SwitchedFabricPort {
{
port_id,
endpoint,
name,
capacity_octets_per_tick,
queue_limit_octets,
base_delay,
}
}
///|
pub(all) struct SwitchedFabricTrafficFlow {
flow_id : Int
source : @core.EndpointId
target : @core.EndpointId
label : String
priority : Int
frame_size_octets : Int
start_ns : Int64
period_ns : Int64
burst_count : Int
background : Bool
} derive(Eq, Debug)
///|
pub fn SwitchedFabricTrafficFlow::is_background(
self : SwitchedFabricTrafficFlow,
) -> Bool {
self.background
}
///|
pub(all) struct SwitchedFabricOfflineProfile {
name : String
queue_discipline : SwitchedFabricQueueDiscipline
ports : Array[SwitchedFabricPort]
flows : Array[SwitchedFabricTrafficFlow]
reorder_window_ns : Int64
complete_switched_fabric : Bool
live_io_evidence : Bool
real_netload_evidence : Bool
} derive(Eq, Debug)
///|
pub fn SwitchedFabricOfflineProfile::background_flow_count(
self : SwitchedFabricOfflineProfile,
) -> Int {
let mut count = 0
for flow in self.flows {
if flow.is_background() {
count += 1
}
}
count
}
///|
pub(all) struct SwitchedFabricFrameRecord {
record_id : Int
flow_id : Int
flow_label : String
source : @core.EndpointId
target : @core.EndpointId
output_port : @core.PortId
priority : Int
frame_size_octets : Int
enqueue_time : @core.VTime
depart_time : @core.VTime?
arrive_time : @core.VTime?
serialization_delay : @core.Duration
queue_occupancy_before : Int
queue_occupancy_after : Int
jitter_ns : Int64
dropped : Bool
reordered : Bool
background : Bool
} derive(Eq, Debug)
///|
pub(all) struct SwitchedFabricOfflineRun {
seed : Int
profile : SwitchedFabricOfflineProfile
frames : Array[SwitchedFabricFrameRecord]
trace : @trace.TraceLog
digest : @core.SimDigest
replay_count : Int
delivered_count : Int
dropped_count : Int
reordered_count : Int
jittered_count : Int
background_frame_count : Int
total_payload_octets : Int
total_background_octets : Int
netload_octets : Int
max_queue_occupancy_octets : Int
} derive(Debug)
///|
pub fn SwitchedFabricOfflineRun::passes(
self : SwitchedFabricOfflineRun,
) -> Bool {
self.profile.complete_switched_fabric &&
!self.profile.live_io_evidence &&
!self.profile.real_netload_evidence &&
self.frames.length() >= 5 &&
self.delivered_count > 0 &&
self.dropped_count > 0 &&
self.reordered_count > 0 &&
self.jittered_count > 0 &&
self.background_frame_count > 0 &&
self.netload_octets ==
self.total_payload_octets + self.total_background_octets &&
self.max_queue_occupancy_octets > 0 &&
self.trace.len() > self.frames.length() &&
self.replay_count == self.trace.len()
}
///|
pub(all) struct SwitchedFabricOfflineReport {
profile_name : String
seed : Int
queue_discipline : SwitchedFabricQueueDiscipline
trace_event_count : Int
replay_count : Int
same_seed_stable : Bool
digest : @core.SimDigest
delivered_count : Int
dropped_count : Int
reordered_count : Int
jittered_count : Int
background_frame_count : Int
netload_octets : Int
max_queue_occupancy_octets : Int
complete_switched_fabric : Bool
live_io_evidence : Bool
real_netload_evidence : Bool
} derive(Eq, Debug)
///|
pub fn SwitchedFabricOfflineReport::passes(
self : SwitchedFabricOfflineReport,
) -> Bool {
self.trace_event_count > 0 &&
self.replay_count == self.trace_event_count &&
self.same_seed_stable &&
self.delivered_count > 0 &&
self.dropped_count > 0 &&
self.reordered_count > 0 &&
self.jittered_count > 0 &&
self.background_frame_count > 0 &&
self.netload_octets > 0 &&
self.max_queue_occupancy_octets > 0 &&
self.complete_switched_fabric &&
!self.live_io_evidence &&
!self.real_netload_evidence
}
///|
pub fn switched_fabric_offline_profile(
seed? : Int = 704,
) -> SwitchedFabricOfflineProfile {
{
name: "switched-fabric-offline-\{switched_normalized_seed(seed)}",
queue_discipline: FifoQueue,
ports: [
SwitchedFabricPort::new(
port_id=@core.PortId(1),
endpoint=@core.EndpointId(1),
name="controller",
capacity_octets_per_tick=96,
queue_limit_octets=512,
base_delay=@core.Duration::from_ns(5L),
),
SwitchedFabricPort::new(
port_id=@core.PortId(2),
endpoint=@core.EndpointId(2),
name="drive-a",
capacity_octets_per_tick=64,
queue_limit_octets=256,
base_delay=@core.Duration::from_ns(8L),
),
SwitchedFabricPort::new(
port_id=@core.PortId(3),
endpoint=@core.EndpointId(3),
name="drive-b",
capacity_octets_per_tick=64,
queue_limit_octets=256,
base_delay=@core.Duration::from_ns(8L),
),
],
flows: [
{
flow_id: 2,
source: @core.EndpointId(3),
target: @core.EndpointId(1),
label: "background.diagnostic.burst",
priority: 4,
frame_size_octets: 192,
start_ns: 55L,
period_ns: 90L,
burst_count: 2,
background: true,
},
{
flow_id: 1,
source: @core.EndpointId(1),
target: @core.EndpointId(2),
label: "cyclic.control",
priority: 1,
frame_size_octets: 80,
start_ns: 10L,
period_ns: 20L,
burst_count: 3,
background: false,
},
{
flow_id: 3,
source: @core.EndpointId(2),
target: @core.EndpointId(1),
label: "acyclic.record",
priority: 3,
frame_size_octets: 144,
start_ns: 70L,
period_ns: 0L,
burst_count: 1,
background: true,
},
{
flow_id: 4,
source: @core.EndpointId(1),
target: @core.EndpointId(3),
label: "oversize.queue-drop",
priority: 2,
frame_size_octets: 384,
start_ns: 12L,
period_ns: 0L,
burst_count: 1,
background: false,
},
],
reorder_window_ns: 12L,
complete_switched_fabric: true,
live_io_evidence: false,
real_netload_evidence: false,
}
}
///|
pub fn run_switched_fabric_offline(
seed? : Int = 704,
) -> SwitchedFabricOfflineRun {
let profile = switched_fabric_offline_profile(seed~)
let frames : Array[SwitchedFabricFrameRecord] = []
let trace = @trace.TraceLog::new()
let queue_end_by_port : Array[Int64] = []
let queue_occupancy_by_port : Array[Int] = []
for _port in profile.ports {
queue_end_by_port.push(0L)
queue_occupancy_by_port.push(0)
}
let mut record_id = 1
let mut event_id = 1
let mut delivered_count = 0
let mut dropped_count = 0
let mut reordered_count = 0
let mut jittered_count = 0
let mut background_frame_count = 0
let mut total_payload_octets = 0
let mut total_background_octets = 0
let mut max_queue_occupancy_octets = 0
let mut last_arrive_ns = 0L
for flow in profile.flows {
let bursts = if flow.burst_count <= 0 { 1 } else { flow.burst_count }
for burst in 0.. enqueue_ns {
queue_occupancy_by_port[port_index]
} else {
0
}
let dropped = queue_before + flow.frame_size_octets >
port.queue_limit_octets
let serialization = serialization_delay(
flow.frame_size_octets,
port.capacity_octets_per_tick,
)
let depart_ns = if dropped {
0L
} else {
max_i64(enqueue_ns, queue_end_by_port[port_index]) + serialization.ns()
}
let arrive_ns = if dropped {
0L
} else {
depart_ns + port.base_delay.ns() + jitter_ns
}
let reordered = !dropped &&
last_arrive_ns > 0L &&
arrive_ns + profile.reorder_window_ns < last_arrive_ns
let queue_after = if dropped {
queue_before
} else {
min_int(port.queue_limit_octets, queue_before + flow.frame_size_octets)
}
let record : SwitchedFabricFrameRecord = {
record_id,
flow_id: flow.flow_id,
flow_label: flow.label,
source: flow.source,
target: flow.target,
output_port: port.port_id,
priority: flow.priority,
frame_size_octets: flow.frame_size_octets,
enqueue_time: @core.VTime::from_ns(enqueue_ns),
depart_time: if dropped {
None
} else {
Some(@core.VTime::from_ns(depart_ns))
},
arrive_time: if dropped {
None
} else {
Some(@core.VTime::from_ns(arrive_ns))
},
serialization_delay: serialization,
queue_occupancy_before: queue_before,
queue_occupancy_after: queue_after,
jitter_ns,
dropped,
reordered,
background: flow.background,
}
frames.push(record)
event_id = append_switched_frame_trace(trace, record, seed~, event_id~)
if dropped {
dropped_count += 1
} else {
delivered_count += 1
queue_end_by_port[port_index] = depart_ns
queue_occupancy_by_port[port_index] = flow.frame_size_octets
last_arrive_ns = arrive_ns
}
if reordered {
reordered_count += 1
}
if jitter_ns > 0L {
jittered_count += 1
}
if flow.background {
background_frame_count += 1
total_background_octets += flow.frame_size_octets
} else {
total_payload_octets += flow.frame_size_octets
}
if queue_after > max_queue_occupancy_octets {
max_queue_occupancy_octets = queue_after
}
record_id += 1
}
}
append_switched_netload_trace(
trace,
event_id~,
seed~,
total_payload_octets~,
total_background_octets~,
max_queue_occupancy_octets~,
)
let digest = trace.portable_digest(seed~)
{
seed,
profile,
frames,
trace,
digest,
replay_count: switched_replay_count(trace),
delivered_count,
dropped_count,
reordered_count,
jittered_count,
background_frame_count,
total_payload_octets,
total_background_octets,
netload_octets: total_payload_octets + total_background_octets,
max_queue_occupancy_octets,
}
}
///|
pub fn switched_fabric_offline_report(
seed? : Int = 704,
) -> SwitchedFabricOfflineReport {
let run = run_switched_fabric_offline(seed~)
let same_seed = run.digest == run_switched_fabric_offline(seed~).digest
{
profile_name: run.profile.name,
seed,
queue_discipline: run.profile.queue_discipline,
trace_event_count: run.trace.len(),
replay_count: run.replay_count,
same_seed_stable: same_seed,
digest: run.digest,
delivered_count: run.delivered_count,
dropped_count: run.dropped_count,
reordered_count: run.reordered_count,
jittered_count: run.jittered_count,
background_frame_count: run.background_frame_count,
netload_octets: run.netload_octets,
max_queue_occupancy_octets: run.max_queue_occupancy_octets,
complete_switched_fabric: run.profile.complete_switched_fabric,
live_io_evidence: run.profile.live_io_evidence,
real_netload_evidence: run.profile.real_netload_evidence,
}
}
///|
pub(all) enum SwitchedFabricIrtPhaseKind {
IrtGreenPhase
IrtRedPhase
} derive(Eq, Debug)
///|
pub fn SwitchedFabricIrtPhaseKind::label(
self : SwitchedFabricIrtPhaseKind,
) -> String {
match self {
IrtGreenPhase => "green"
IrtRedPhase => "red"
}
}
///|
pub(all) struct SwitchedFabricIrtPhaseWindow {
start_ns : Int64
end_ns : Int64
phase : SwitchedFabricIrtPhaseKind
} derive(Eq, Debug)
///|
pub(all) struct SwitchedFabricQosIrtProfile {
name : String
queue_discipline : SwitchedFabricQueueDiscipline
ports : Array[SwitchedFabricPort]
flows : Array[SwitchedFabricTrafficFlow]
phase_table : Array[SwitchedFabricIrtPhaseWindow]
red_phase_drop_priority_floor : Int
complete_switched_fabric : Bool
live_io_evidence : Bool
real_netload_evidence : Bool
real_irt_evidence : Bool
} derive(Eq, Debug)
///|
pub(all) struct SwitchedFabricQosIrtFrameRecord {
record_id : Int
flow_id : Int
flow_label : String
output_port : @core.PortId
priority : Int
enqueue_time : @core.VTime
effective_enqueue_time : @core.VTime
depart_time : @core.VTime?
arrive_time : @core.VTime?
phase_at_enqueue : SwitchedFabricIrtPhaseKind
held_by_red_phase : Bool
dropped_by_red_phase : Bool
released_in_green_phase : Bool
queue_occupancy_before : Int
queue_occupancy_after : Int
frame_size_octets : Int
background : Bool
} derive(Eq, Debug)
///|
pub(all) struct SwitchedFabricQosIrtRun {
seed : Int
profile : SwitchedFabricQosIrtProfile
frames : Array[SwitchedFabricQosIrtFrameRecord]
trace : @trace.TraceLog
digest : @core.SimDigest
replay_count : Int
delivered_count : Int
dropped_count : Int
red_phase_hold_count : Int
red_phase_drop_count : Int
green_phase_release_count : Int
strict_priority_selection_count : Int
netload_octets : Int
high_priority_before_low_priority : Bool
} derive(Debug)
///|
pub fn SwitchedFabricQosIrtRun::passes(self : SwitchedFabricQosIrtRun) -> Bool {
self.profile.queue_discipline == StrictPriorityQueue &&
self.profile.complete_switched_fabric &&
!self.profile.live_io_evidence &&
!self.profile.real_netload_evidence &&
!self.profile.real_irt_evidence &&
self.frames.length() >= 5 &&
self.delivered_count > 0 &&
self.dropped_count > 0 &&
self.red_phase_hold_count > 0 &&
self.red_phase_drop_count > 0 &&
self.green_phase_release_count > 0 &&
self.strict_priority_selection_count > 0 &&
self.high_priority_before_low_priority &&
self.netload_octets > 0 &&
self.trace.len() > self.frames.length() &&
self.replay_count == self.trace.len()
}
///|
pub(all) struct SwitchedFabricQosIrtReport {
profile_name : String
seed : Int
queue_discipline : SwitchedFabricQueueDiscipline
trace_event_count : Int
replay_count : Int
same_seed_stable : Bool
digest : @core.SimDigest
delivered_count : Int
dropped_count : Int
red_phase_hold_count : Int
red_phase_drop_count : Int
green_phase_release_count : Int
strict_priority_selection_count : Int
netload_octets : Int
high_priority_before_low_priority : Bool
complete_switched_fabric : Bool
live_io_evidence : Bool
real_netload_evidence : Bool
real_irt_evidence : Bool
} derive(Eq, Debug)
///|
pub fn SwitchedFabricQosIrtReport::passes(
self : SwitchedFabricQosIrtReport,
) -> Bool {
self.trace_event_count > 0 &&
self.replay_count == self.trace_event_count &&
self.same_seed_stable &&
self.queue_discipline == StrictPriorityQueue &&
self.delivered_count > 0 &&
self.dropped_count > 0 &&
self.red_phase_hold_count > 0 &&
self.red_phase_drop_count > 0 &&
self.green_phase_release_count > 0 &&
self.strict_priority_selection_count > 0 &&
self.high_priority_before_low_priority &&
self.netload_octets > 0 &&
self.complete_switched_fabric &&
!self.live_io_evidence &&
!self.real_netload_evidence &&
!self.real_irt_evidence
}
///|
pub fn switched_fabric_qos_irt_profile(
seed? : Int = 734,
) -> SwitchedFabricQosIrtProfile {
{
name: "switched-fabric-qos-irt-\{switched_normalized_seed(seed)}",
queue_discipline: StrictPriorityQueue,
ports: [
SwitchedFabricPort::new(
port_id=@core.PortId(1),
endpoint=@core.EndpointId(1),
name="controller",
capacity_octets_per_tick=96,
queue_limit_octets=512,
base_delay=@core.Duration::from_ns(5L),
),
SwitchedFabricPort::new(
port_id=@core.PortId(2),
endpoint=@core.EndpointId(2),
name="drive-a",
capacity_octets_per_tick=64,
queue_limit_octets=256,
base_delay=@core.Duration::from_ns(8L),
),
],
flows: [
{
flow_id: 10,
source: @core.EndpointId(3),
target: @core.EndpointId(2),
label: "background.low.same-slot",
priority: 7,
frame_size_octets: 96,
start_ns: 10L,
period_ns: 0L,
burst_count: 1,
background: true,
},
{
flow_id: 11,
source: @core.EndpointId(1),
target: @core.EndpointId(2),
label: "cyclic.high.same-slot",
priority: 1,
frame_size_octets: 64,
start_ns: 10L,
period_ns: 0L,
burst_count: 1,
background: false,
},
{
flow_id: 12,
source: @core.EndpointId(1),
target: @core.EndpointId(2),
label: "irt.red.held",
priority: 1,
frame_size_octets: 80,
start_ns: 24L,
period_ns: 0L,
burst_count: 1,
background: false,
},
{
flow_id: 13,
source: @core.EndpointId(3),
target: @core.EndpointId(2),
label: "netload.red.drop",
priority: 6,
frame_size_octets: 192,
start_ns: 30L,
period_ns: 0L,
burst_count: 1,
background: true,
},
{
flow_id: 14,
source: @core.EndpointId(1),
target: @core.EndpointId(2),
label: "irt.green.release",
priority: 2,
frame_size_octets: 72,
start_ns: 46L,
period_ns: 0L,
burst_count: 1,
background: false,
},
],
phase_table: [
{ start_ns: 0L, end_ns: 20L, phase: IrtGreenPhase },
{ start_ns: 20L, end_ns: 40L, phase: IrtRedPhase },
{ start_ns: 40L, end_ns: 90L, phase: IrtGreenPhase },
],
red_phase_drop_priority_floor: 3,
complete_switched_fabric: true,
live_io_evidence: false,
real_netload_evidence: false,
real_irt_evidence: false,
}
}
///|
pub fn run_switched_fabric_qos_irt_offline(
seed? : Int = 734,
) -> SwitchedFabricQosIrtRun {
let profile = switched_fabric_qos_irt_profile(seed~)
let ordered = ordered_qos_irt_flows(profile)
let frames : Array[SwitchedFabricQosIrtFrameRecord] = []
let trace = @trace.TraceLog::new()
let queue_end_by_port : Array[Int64] = []
let queue_occupancy_by_port : Array[Int] = []
for _port in profile.ports {
queue_end_by_port.push(0L)
queue_occupancy_by_port.push(0)
}
let mut event_id = 2000
let mut record_id = 1
let mut delivered_count = 0
let mut dropped_count = 0
let mut red_phase_hold_count = 0
let mut red_phase_drop_count = 0
let mut green_phase_release_count = 0
let strict_priority_selection_count = strict_priority_same_slot_count(profile)
let mut netload_octets = 0
for flow in ordered {
let port_index = find_qos_irt_port_index(profile, flow.target)
let port = profile.ports[port_index]
let phase = phase_at(profile, flow.start_ns)
let held_by_red_phase = phase == IrtRedPhase &&
flow.priority < profile.red_phase_drop_priority_floor
let dropped_by_red_phase = phase == IrtRedPhase &&
flow.priority >= profile.red_phase_drop_priority_floor
let effective_enqueue_ns = if held_by_red_phase {
next_green_start_ns(profile, flow.start_ns)
} else {
flow.start_ns
}
let queue_before = if queue_end_by_port[port_index] > effective_enqueue_ns {
queue_occupancy_by_port[port_index]
} else {
0
}
let serialization = serialization_delay(
flow.frame_size_octets,
port.capacity_octets_per_tick,
)
let depart_ns = if dropped_by_red_phase {
0L
} else {
max_i64(effective_enqueue_ns, queue_end_by_port[port_index]) +
serialization.ns()
}
let arrive_ns = if dropped_by_red_phase {
0L
} else {
depart_ns + port.base_delay.ns()
}
let released_in_green_phase = held_by_red_phase &&
phase_at(profile, effective_enqueue_ns) == IrtGreenPhase
let queue_after = if dropped_by_red_phase {
queue_before
} else {
min_int(port.queue_limit_octets, queue_before + flow.frame_size_octets)
}
let record : SwitchedFabricQosIrtFrameRecord = {
record_id,
flow_id: flow.flow_id,
flow_label: flow.label,
output_port: port.port_id,
priority: flow.priority,
enqueue_time: @core.VTime::from_ns(flow.start_ns),
effective_enqueue_time: @core.VTime::from_ns(effective_enqueue_ns),
depart_time: if dropped_by_red_phase {
None
} else {
Some(@core.VTime::from_ns(depart_ns))
},
arrive_time: if dropped_by_red_phase {
None
} else {
Some(@core.VTime::from_ns(arrive_ns))
},
phase_at_enqueue: phase,
held_by_red_phase,
dropped_by_red_phase,
released_in_green_phase,
queue_occupancy_before: queue_before,
queue_occupancy_after: queue_after,
frame_size_octets: flow.frame_size_octets,
background: flow.background,
}
frames.push(record)
event_id = append_qos_irt_frame_trace(trace, record, seed~, event_id~)
if dropped_by_red_phase {
dropped_count += 1
red_phase_drop_count += 1
} else {
delivered_count += 1
queue_end_by_port[port_index] = depart_ns
queue_occupancy_by_port[port_index] = flow.frame_size_octets
}
if held_by_red_phase {
red_phase_hold_count += 1
}
if released_in_green_phase {
green_phase_release_count += 1
}
netload_octets += flow.frame_size_octets
record_id += 1
}
append_qos_irt_summary_trace(
trace,
event_id~,
seed~,
red_phase_hold_count~,
red_phase_drop_count~,
green_phase_release_count~,
)
let digest = trace.portable_digest(seed~)
{
seed,
profile,
frames,
trace,
digest,
replay_count: switched_replay_count(trace),
delivered_count,
dropped_count,
red_phase_hold_count,
red_phase_drop_count,
green_phase_release_count,
strict_priority_selection_count,
netload_octets,
high_priority_before_low_priority: high_priority_before_low_priority(frames),
}
}
///|
pub fn switched_fabric_qos_irt_report(
seed? : Int = 734,
) -> SwitchedFabricQosIrtReport {
let run = run_switched_fabric_qos_irt_offline(seed~)
let same_seed = run.digest ==
run_switched_fabric_qos_irt_offline(seed~).digest
{
profile_name: run.profile.name,
seed,
queue_discipline: run.profile.queue_discipline,
trace_event_count: run.trace.len(),
replay_count: run.replay_count,
same_seed_stable: same_seed,
digest: run.digest,
delivered_count: run.delivered_count,
dropped_count: run.dropped_count,
red_phase_hold_count: run.red_phase_hold_count,
red_phase_drop_count: run.red_phase_drop_count,
green_phase_release_count: run.green_phase_release_count,
strict_priority_selection_count: run.strict_priority_selection_count,
netload_octets: run.netload_octets,
high_priority_before_low_priority: run.high_priority_before_low_priority,
complete_switched_fabric: run.profile.complete_switched_fabric,
live_io_evidence: run.profile.live_io_evidence,
real_netload_evidence: run.profile.real_netload_evidence,
real_irt_evidence: run.profile.real_irt_evidence,
}
}
///|
pub fn SwitchedFabricQosIrtRun::timeline_text(
self : SwitchedFabricQosIrtRun,
) -> String {
let buf = StringBuilder::new()
buf.write_string("fabric=switched-qos-irt-offline|seed=")
buf.write_string(self.seed.to_string())
buf.write_string("|queue=")
buf.write_string(self.profile.queue_discipline.label())
buf.write_string("|frames=")
buf.write_string(self.frames.length().to_string())
buf.write_string("|red_hold=")
buf.write_string(self.red_phase_hold_count.to_string())
buf.write_string("|red_drop=")
buf.write_string(self.red_phase_drop_count.to_string())
buf.write_string("|green_release=")
buf.write_string(self.green_phase_release_count.to_string())
buf.write_string("|strict_priority=")
buf.write_string(self.strict_priority_selection_count.to_string())
for frame in self.frames {
buf.write_char('\n')
buf.write_string("frame=")
buf.write_string(frame.record_id.to_string())
buf.write_string("|flow=")
buf.write_string(frame.flow_label)
buf.write_string("|priority=")
buf.write_string(frame.priority.to_string())
buf.write_string("|phase=")
buf.write_string(frame.phase_at_enqueue.label())
buf.write_string("|enqueue_ns=")
buf.write_string(frame.enqueue_time.ns().to_string())
buf.write_string("|effective_enqueue_ns=")
buf.write_string(frame.effective_enqueue_time.ns().to_string())
buf.write_string("|red_hold=")
buf.write_string(frame.held_by_red_phase.to_string())
buf.write_string("|red_drop=")
buf.write_string(frame.dropped_by_red_phase.to_string())
buf.write_string("|green_release=")
buf.write_string(frame.released_in_green_phase.to_string())
}
buf.to_string()
}
///|
pub fn SwitchedFabricOfflineRun::timeline_text(
self : SwitchedFabricOfflineRun,
) -> String {
let buf = StringBuilder::new()
buf.write_string("fabric=switched-offline|seed=")
buf.write_string(self.seed.to_string())
buf.write_string("|queue=")
buf.write_string(self.profile.queue_discipline.label())
buf.write_string("|frames=")
buf.write_string(self.frames.length().to_string())
buf.write_string("|delivered=")
buf.write_string(self.delivered_count.to_string())
buf.write_string("|dropped=")
buf.write_string(self.dropped_count.to_string())
buf.write_string("|reordered=")
buf.write_string(self.reordered_count.to_string())
buf.write_string("|netload_octets=")
buf.write_string(self.netload_octets.to_string())
for frame in self.frames {
buf.write_char('\n')
buf.write_string("frame=")
buf.write_string(frame.record_id.to_string())
buf.write_string("|flow=")
buf.write_string(frame.flow_label)
buf.write_string("|port=")
buf.write_string(frame.output_port_text())
buf.write_string("|enqueue_ns=")
buf.write_string(frame.enqueue_time.ns().to_string())
buf.write_string("|queue_before=")
buf.write_string(frame.queue_occupancy_before.to_string())
buf.write_string("|dropped=")
buf.write_string(frame.dropped.to_string())
buf.write_string("|reordered=")
buf.write_string(frame.reordered.to_string())
}
buf.to_string()
}
///|
fn SwitchedFabricFrameRecord::output_port_text(
self : SwitchedFabricFrameRecord,
) -> String {
match self.output_port {
@core.PortId(value) => value.to_string()
}
}
///|
fn append_switched_frame_trace(
trace : @trace.TraceLog,
frame : SwitchedFabricFrameRecord,
seed~ : Int,
event_id~ : Int,
) -> Int {
let mut next = event_id
trace.append(
switched_trace_event(
event_id=next,
parent_id=None,
vtime=frame.enqueue_time,
direction=@trace.Tx,
payload_digest=Some(frame.frame_size_octets),
seed~,
label="fabric.switched.tx." + frame.flow_label,
),
)
next += 1
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=frame.enqueue_time,
direction=@trace.Resource,
payload_digest=Some(frame.queue_occupancy_after),
seed~,
label="fabric.switched.queue." + frame.flow_label,
),
)
next += 1
if frame.dropped {
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=frame.enqueue_time,
direction=@trace.Fault,
payload_digest=Some(frame.queue_occupancy_after),
seed~,
label="fabric.switched.drop.queue",
),
)
next += 1
} else {
guard frame.arrive_time is Some(arrive) else { return next }
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=arrive,
direction=@trace.Rx,
payload_digest=Some(frame.frame_size_octets),
seed~,
label="fabric.switched.rx." + frame.flow_label,
),
)
next += 1
if frame.reordered {
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=arrive,
direction=@trace.Probe,
payload_digest=Some(frame.priority),
seed~,
label="fabric.switched.reorder",
),
)
next += 1
}
}
next
}
///|
fn append_switched_netload_trace(
trace : @trace.TraceLog,
event_id~ : Int,
seed~ : Int,
total_payload_octets~ : Int,
total_background_octets~ : Int,
max_queue_occupancy_octets~ : Int,
) -> Unit {
trace.append(
switched_trace_event(
event_id~,
parent_id=None,
vtime=@core.VTime::from_ns(999L),
direction=@trace.Probe,
payload_digest=Some(
total_payload_octets +
total_background_octets +
max_queue_occupancy_octets,
),
seed~,
label="fabric.switched.netload.accounting",
),
)
}
///|
fn ordered_qos_irt_flows(
profile : SwitchedFabricQosIrtProfile,
) -> Array[SwitchedFabricTrafficFlow] {
let ordered : Array[SwitchedFabricTrafficFlow] = []
let used : Array[Bool] = []
for _flow in profile.flows {
used.push(false)
}
while ordered.length() < profile.flows.length() {
let mut selected = -1
for index, flow in profile.flows {
if !used[index] &&
(selected < 0 || qos_irt_flow_precedes(flow, profile.flows[selected])) {
selected = index
}
}
if selected >= 0 {
ordered.push(profile.flows[selected])
used[selected] = true
} else {
return ordered
}
}
ordered
}
///|
fn qos_irt_flow_precedes(
left : SwitchedFabricTrafficFlow,
right : SwitchedFabricTrafficFlow,
) -> Bool {
if left.start_ns != right.start_ns {
left.start_ns < right.start_ns
} else if left.target != right.target {
left.target.value() < right.target.value()
} else if left.priority != right.priority {
left.priority < right.priority
} else {
left.flow_id < right.flow_id
}
}
///|
fn strict_priority_same_slot_count(
profile : SwitchedFabricQosIrtProfile,
) -> Int {
let mut count = 0
for index, flow in profile.flows {
for other_index, other in profile.flows {
if index < other_index &&
flow.target == other.target &&
flow.start_ns == other.start_ns &&
flow.priority != other.priority {
count += 1
}
}
}
count
}
///|
fn phase_at(
profile : SwitchedFabricQosIrtProfile,
time_ns : Int64,
) -> SwitchedFabricIrtPhaseKind {
for window in profile.phase_table {
if time_ns >= window.start_ns && time_ns < window.end_ns {
return window.phase
}
}
IrtGreenPhase
}
///|
fn next_green_start_ns(
profile : SwitchedFabricQosIrtProfile,
time_ns : Int64,
) -> Int64 {
for window in profile.phase_table {
if window.phase == IrtGreenPhase && window.start_ns >= time_ns {
return window.start_ns
}
}
time_ns
}
///|
fn find_qos_irt_port_index(
profile : SwitchedFabricQosIrtProfile,
endpoint : @core.EndpointId,
) -> Int {
for index, port in profile.ports {
if port.endpoint == endpoint {
return index
}
}
0
}
///|
fn high_priority_before_low_priority(
frames : Array[SwitchedFabricQosIrtFrameRecord],
) -> Bool {
let mut high_index = -1
let mut low_index = -1
for index, frame in frames {
if frame.flow_label == "cyclic.high.same-slot" {
high_index = index
}
if frame.flow_label == "background.low.same-slot" {
low_index = index
}
}
high_index >= 0 && low_index >= 0 && high_index < low_index
}
///|
fn append_qos_irt_frame_trace(
trace : @trace.TraceLog,
frame : SwitchedFabricQosIrtFrameRecord,
seed~ : Int,
event_id~ : Int,
) -> Int {
let mut next = event_id
trace.append(
switched_trace_event(
event_id=next,
parent_id=None,
vtime=frame.enqueue_time,
direction=@trace.Tx,
payload_digest=Some(frame.priority),
seed~,
label="fabric.switched.qos.tx." + frame.flow_label,
),
)
next += 1
if frame.flow_label == "cyclic.high.same-slot" {
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=frame.enqueue_time,
direction=@trace.Resource,
payload_digest=Some(frame.priority),
seed~,
label="fabric.switched.qos.priority-select",
),
)
next += 1
}
if frame.held_by_red_phase {
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=frame.enqueue_time,
direction=@trace.Resource,
payload_digest=Some(frame.frame_size_octets),
seed~,
label="fabric.switched.irt.red-hold",
),
)
next += 1
}
if frame.dropped_by_red_phase {
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=frame.enqueue_time,
direction=@trace.Fault,
payload_digest=Some(frame.frame_size_octets),
seed~,
label="fabric.switched.irt.red-drop",
),
)
next += 1
} else {
if frame.released_in_green_phase {
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=frame.effective_enqueue_time,
direction=@trace.Resource,
payload_digest=Some(frame.frame_size_octets),
seed~,
label="fabric.switched.irt.green-release",
),
)
next += 1
}
guard frame.arrive_time is Some(arrive) else { return next }
trace.append(
switched_trace_event(
event_id=next,
parent_id=Some(frame.record_id),
vtime=arrive,
direction=@trace.Rx,
payload_digest=Some(frame.frame_size_octets),
seed~,
label="fabric.switched.qos.rx." + frame.flow_label,
),
)
next += 1
}
next
}
///|
fn append_qos_irt_summary_trace(
trace : @trace.TraceLog,
event_id~ : Int,
seed~ : Int,
red_phase_hold_count~ : Int,
red_phase_drop_count~ : Int,
green_phase_release_count~ : Int,
) -> Unit {
trace.append(
switched_trace_event(
event_id~,
parent_id=None,
vtime=@core.VTime::from_ns(120L),
direction=@trace.Probe,
payload_digest=Some(
red_phase_hold_count + red_phase_drop_count + green_phase_release_count,
),
seed~,
label="fabric.switched.qos-irt.summary",
),
)
}
///|
fn switched_trace_event(
event_id~ : Int,
parent_id~ : Int?,
vtime~ : @core.VTime,
direction~ : @trace.TraceDirection,
payload_digest~ : Int?,
seed~ : Int,
label~ : String,
) -> @trace.TraceEvent {
@trace.TraceEvent::make(
event_id~,
parent_id~,
vtime~,
clock_domain="sim",
raw_ns=vtime.ns(),
node_id="switched-fabric",
medium_id="switched-fabric-offline",
channel_id=Some(@core.ChannelId(event_id)),
direction~,
payload_digest~,
rng_step=event_id,
seed~,
backend=@core.SimNative,
label~,
)
}
///|
fn find_switched_port_index(
profile : SwitchedFabricOfflineProfile,
endpoint : @core.EndpointId,
) -> Int {
for index, port in profile.ports {
if port.endpoint == endpoint {
return index
}
}
0
}
///|
fn serialization_delay(
frame_size_octets : Int,
capacity_octets_per_tick : Int,
) -> @core.Duration {
let capacity = if capacity_octets_per_tick <= 0 {
1
} else {
capacity_octets_per_tick
}
let ticks = (frame_size_octets + capacity - 1) / capacity
@core.Duration::from_ns(Int64::from_int(ticks))
}
///|
fn switched_jitter_ns(seed : Int, flow_id : Int, burst : Int) -> Int64 {
let value = switched_normalized_seed(seed + flow_id * 17 + burst * 31) % 5
Int64::from_int(value * 3)
}
///|
fn switched_replay_count(trace : @trace.TraceLog) -> Int {
let replay = @trace.Replay::from_log(trace)
let mut count = 0
while !replay.is_exhausted() {
ignore(replay.next_event())
count += 1
}
count
}
///|
fn switched_normalized_seed(seed : Int) -> Int {
if seed < 0 {
-seed
} else {
seed
}
}
///|
fn max_i64(left : Int64, right : Int64) -> Int64 {
if left >= right {
left
} else {
right
}
}
///|
fn min_int(left : Int, right : Int) -> Int {
if left <= right {
left
} else {
right
}
}