use std::collections::VecDeque;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ChannelState {
#[default]
Idle,
Waiting,
Ready,
Active,
Complete,
Error,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum LaneState {
#[default]
Available,
Active,
FrameComplete,
UtteranceComplete,
}
#[derive(Clone, Debug)]
pub struct Channel<T> {
id: usize,
state: ChannelState,
lane: Option<usize>,
user_data: Option<T>,
frame_index: usize,
total_frames: Option<usize>,
token_count: usize,
}
impl<T> Channel<T> {
pub fn new(id: usize) -> Self {
Self {
id,
state: ChannelState::Idle,
lane: None,
user_data: None,
frame_index: 0,
total_frames: None,
token_count: 0,
}
}
pub fn id(&self) -> usize {
self.id
}
pub fn state(&self) -> ChannelState {
self.state
}
pub fn lane(&self) -> Option<usize> {
self.lane
}
pub fn user_data(&self) -> Option<&T> {
self.user_data.as_ref()
}
pub fn user_data_mut(&mut self) -> Option<&mut T> {
self.user_data.as_mut()
}
pub fn frame_index(&self) -> usize {
self.frame_index
}
pub fn total_frames(&self) -> Option<usize> {
self.total_frames
}
pub fn token_count(&self) -> usize {
self.token_count
}
pub fn is_idle(&self) -> bool {
self.state == ChannelState::Idle
}
pub fn is_active(&self) -> bool {
self.state == ChannelState::Active
}
pub fn is_ready(&self) -> bool {
self.state == ChannelState::Ready
}
pub fn start_utterance(&mut self, user_data: T, total_frames: Option<usize>) {
self.state = ChannelState::Ready;
self.user_data = Some(user_data);
self.frame_index = 0;
self.total_frames = total_frames;
self.token_count = 0;
}
pub fn assign_lane(&mut self, lane: usize) {
self.lane = Some(lane);
self.state = ChannelState::Active;
}
pub fn release_lane(&mut self) {
self.lane = None;
if self.state == ChannelState::Active {
self.state = ChannelState::Waiting;
}
}
pub fn advance_frame(&mut self, token_count: usize) {
self.frame_index += 1;
self.token_count = token_count;
}
pub fn complete(&mut self) -> Option<T> {
self.state = ChannelState::Complete;
self.lane = None;
self.user_data.take()
}
pub fn error(&mut self) {
self.state = ChannelState::Error;
self.lane = None;
}
pub fn reset(&mut self) {
self.state = ChannelState::Idle;
self.lane = None;
self.user_data = None;
self.frame_index = 0;
self.total_frames = None;
self.token_count = 0;
}
}
#[derive(Clone, Debug)]
pub struct Lane {
id: usize,
state: LaneState,
channel: Option<usize>,
token_count: usize,
max_tokens: usize,
}
impl Lane {
pub fn new(id: usize, max_tokens: usize) -> Self {
Self {
id,
state: LaneState::Available,
channel: None,
token_count: 0,
max_tokens,
}
}
pub fn id(&self) -> usize {
self.id
}
pub fn state(&self) -> LaneState {
self.state
}
pub fn channel(&self) -> Option<usize> {
self.channel
}
pub fn token_count(&self) -> usize {
self.token_count
}
pub fn max_tokens(&self) -> usize {
self.max_tokens
}
pub fn is_available(&self) -> bool {
self.state == LaneState::Available
}
pub fn is_active(&self) -> bool {
self.state == LaneState::Active
}
pub fn assign_channel(&mut self, channel: usize) {
self.channel = Some(channel);
self.state = LaneState::Active;
self.token_count = 0;
}
pub fn release_channel(&mut self) -> Option<usize> {
self.state = LaneState::Available;
self.token_count = 0;
self.channel.take()
}
pub fn update_tokens(&mut self, count: usize) {
self.token_count = count.min(self.max_tokens);
}
pub fn frame_complete(&mut self) {
self.state = LaneState::FrameComplete;
}
pub fn utterance_complete(&mut self) {
self.state = LaneState::UtteranceComplete;
}
pub fn continue_decoding(&mut self) {
if self.state == LaneState::FrameComplete {
self.state = LaneState::Active;
}
}
}
#[derive(Clone, Debug)]
pub struct DecoderConfig {
pub max_channels: usize,
pub max_lanes: usize,
pub max_tokens: usize,
pub beam_width: f32,
}
impl Default for DecoderConfig {
fn default() -> Self {
Self {
max_channels: 1000,
max_lanes: 32,
max_tokens: 10000,
beam_width: 16.0,
}
}
}
impl DecoderConfig {
pub fn edge_device() -> Self {
Self {
max_channels: 10,
max_lanes: 1,
max_tokens: 10000,
beam_width: 10.0,
}
}
pub fn datacenter() -> Self {
Self {
max_channels: 5000,
max_lanes: 500,
max_tokens: 10000,
beam_width: 15.0,
}
}
pub fn estimate_memory(&self) -> usize {
let alpha = self.max_tokens;
let n_c = self.max_channels;
let n_l = self.max_lanes;
64 * alpha * n_c + 544 * alpha * n_l + 1024 * n_l
}
}
pub struct BatchedDecoder<T> {
channels: Vec<Channel<T>>,
lanes: Vec<Lane>,
ready_queue: VecDeque<usize>,
config: DecoderConfig,
}
impl<T> BatchedDecoder<T> {
pub fn new(config: DecoderConfig) -> Self {
let channels = (0..config.max_channels).map(Channel::new).collect();
let lanes = (0..config.max_lanes)
.map(|id| Lane::new(id, config.max_tokens))
.collect();
Self {
channels,
lanes,
ready_queue: VecDeque::new(),
config,
}
}
pub fn config(&self) -> &DecoderConfig {
&self.config
}
pub fn channel(&self, id: usize) -> Option<&Channel<T>> {
self.channels.get(id)
}
pub fn channel_mut(&mut self, id: usize) -> Option<&mut Channel<T>> {
self.channels.get_mut(id)
}
pub fn lane(&self, id: usize) -> Option<&Lane> {
self.lanes.get(id)
}
pub fn lane_mut(&mut self, id: usize) -> Option<&mut Lane> {
self.lanes.get_mut(id)
}
pub fn find_idle_channel(&self) -> Option<usize> {
self.channels.iter().position(|c| c.is_idle())
}
pub fn find_available_lane(&self) -> Option<usize> {
self.lanes.iter().position(|l| l.is_available())
}
pub fn start_utterance(&mut self, user_data: T, total_frames: Option<usize>) -> Option<usize> {
let channel_id = self.find_idle_channel()?;
self.channels[channel_id].start_utterance(user_data, total_frames);
self.ready_queue.push_back(channel_id);
Some(channel_id)
}
pub fn schedule(&mut self) -> Vec<(usize, usize)> {
let mut assignments = Vec::new();
while let Some(channel_id) = self.ready_queue.pop_front() {
if let Some(lane_id) = self.find_available_lane() {
self.channels[channel_id].assign_lane(lane_id);
self.lanes[lane_id].assign_channel(channel_id);
assignments.push((channel_id, lane_id));
} else {
self.ready_queue.push_front(channel_id);
break;
}
}
assignments
}
pub fn active_lanes(&self) -> Vec<usize> {
self.lanes
.iter()
.filter(|l| l.is_active())
.map(|l| l.id())
.collect()
}
pub fn process_frame<F>(&mut self, mut process_fn: F) -> Vec<usize>
where
F: FnMut(usize, usize) -> (usize, bool), {
let mut completed_lanes = Vec::new();
for lane in &mut self.lanes {
if !lane.is_active() {
continue;
}
let channel_id = match lane.channel() {
Some(id) => id,
None => continue,
};
let (token_count, is_complete) = process_fn(lane.id(), channel_id);
lane.update_tokens(token_count);
if is_complete {
lane.utterance_complete();
completed_lanes.push(lane.id());
} else {
lane.frame_complete();
}
}
completed_lanes
}
pub fn continue_decoding(&mut self) {
for lane in &mut self.lanes {
lane.continue_decoding();
}
}
pub fn complete_utterances(&mut self, lane_ids: &[usize]) -> Vec<(usize, Option<T>)> {
let mut results = Vec::new();
for &lane_id in lane_ids {
if let Some(lane) = self.lanes.get_mut(lane_id) {
if let Some(channel_id) = lane.release_channel() {
if let Some(channel) = self.channels.get_mut(channel_id) {
let user_data = channel.complete();
results.push((channel_id, user_data));
}
}
}
}
results
}
pub fn stats(&self) -> DecoderStats {
let active_channels = self.channels.iter().filter(|c| c.is_active()).count();
let ready_channels = self.ready_queue.len();
let active_lanes = self.lanes.iter().filter(|l| l.is_active()).count();
let total_tokens: usize = self.lanes.iter().map(|l| l.token_count()).sum();
DecoderStats {
max_channels: self.config.max_channels,
max_lanes: self.config.max_lanes,
active_channels,
ready_channels,
active_lanes,
total_tokens,
lane_utilization: active_lanes as f64 / self.config.max_lanes as f64,
}
}
}
#[derive(Clone, Debug)]
pub struct DecoderStats {
pub max_channels: usize,
pub max_lanes: usize,
pub active_channels: usize,
pub ready_channels: usize,
pub active_lanes: usize,
pub total_tokens: usize,
pub lane_utilization: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_channel_states() {
let mut channel: Channel<i32> = Channel::new(0);
assert!(channel.is_idle());
channel.start_utterance(42, Some(100));
assert!(channel.is_ready());
assert_eq!(channel.user_data(), Some(&42));
channel.assign_lane(0);
assert!(channel.is_active());
assert_eq!(channel.lane(), Some(0));
channel.advance_frame(50);
assert_eq!(channel.frame_index(), 1);
assert_eq!(channel.token_count(), 50);
let data = channel.complete();
assert_eq!(data, Some(42));
assert_eq!(channel.state(), ChannelState::Complete);
}
#[test]
fn test_lane_states() {
let mut lane = Lane::new(0, 1000);
assert!(lane.is_available());
lane.assign_channel(5);
assert!(lane.is_active());
assert_eq!(lane.channel(), Some(5));
lane.update_tokens(500);
assert_eq!(lane.token_count(), 500);
lane.frame_complete();
assert_eq!(lane.state(), LaneState::FrameComplete);
lane.continue_decoding();
assert!(lane.is_active());
lane.utterance_complete();
assert_eq!(lane.state(), LaneState::UtteranceComplete);
let channel = lane.release_channel();
assert_eq!(channel, Some(5));
assert!(lane.is_available());
}
#[test]
fn test_decoder_config() {
let config = DecoderConfig::default();
assert_eq!(config.max_channels, 1000);
assert_eq!(config.max_lanes, 32);
let edge = DecoderConfig::edge_device();
assert_eq!(edge.max_lanes, 1);
let dc = DecoderConfig::datacenter();
assert_eq!(dc.max_lanes, 500);
}
#[test]
fn test_decoder_config_memory() {
let config = DecoderConfig {
max_channels: 1,
max_lanes: 1,
max_tokens: 10000,
beam_width: 10.0,
};
let mem = config.estimate_memory();
assert!(mem > 6_000_000);
}
#[test]
fn test_batched_decoder_basic() {
let config = DecoderConfig {
max_channels: 10,
max_lanes: 2,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<String> = BatchedDecoder::new(config);
let ch1 = decoder.start_utterance("utt1".to_string(), Some(10));
let ch2 = decoder.start_utterance("utt2".to_string(), Some(20));
assert!(ch1.is_some());
assert!(ch2.is_some());
let assignments = decoder.schedule();
assert_eq!(assignments.len(), 2);
let stats = decoder.stats();
assert_eq!(stats.active_channels, 2);
assert_eq!(stats.active_lanes, 2);
}
#[test]
fn test_batched_decoder_overflow() {
let config = DecoderConfig {
max_channels: 2,
max_lanes: 1,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
decoder.start_utterance(1, None);
decoder.start_utterance(2, None);
let assignments = decoder.schedule();
assert_eq!(assignments.len(), 1);
let stats = decoder.stats();
assert_eq!(stats.active_channels, 1);
assert_eq!(stats.ready_channels, 1);
}
#[test]
fn test_batched_decoder_process_frame() {
let config = DecoderConfig {
max_channels: 10,
max_lanes: 2,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
decoder.start_utterance(1, Some(2));
decoder.start_utterance(2, Some(3));
decoder.schedule();
let total_frames_1 = decoder
.channel(1)
.and_then(|c| c.total_frames())
.unwrap_or(10);
let total_frames_2 = decoder
.channel(2)
.and_then(|c| c.total_frames())
.unwrap_or(10);
let mut frame = 0;
let completed = decoder.process_frame(|_lane, channel| {
frame += 1;
let total = if channel == 1 {
total_frames_1
} else {
total_frames_2
};
(50, frame >= total)
});
assert!(completed.is_empty() || completed.len() <= 2);
}
#[test]
fn test_batched_decoder_complete() {
let config = DecoderConfig {
max_channels: 10,
max_lanes: 2,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<String> = BatchedDecoder::new(config);
decoder.start_utterance("test".to_string(), Some(1));
decoder.schedule();
let completed = decoder.process_frame(|_, _| (10, true));
assert_eq!(completed.len(), 1);
let results = decoder.complete_utterances(&completed);
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, Some("test".to_string()));
}
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn channel_new_is_idle(id in 0usize..1000) {
let channel: Channel<i32> = Channel::new(id);
prop_assert!(channel.is_idle());
prop_assert_eq!(channel.id(), id);
prop_assert_eq!(channel.state(), ChannelState::Idle);
prop_assert!(channel.lane().is_none());
prop_assert!(channel.user_data().is_none());
}
#[test]
fn channel_start_utterance_ready(
id in 0usize..100,
user_data in any::<i32>(),
total_frames in proptest::option::of(1usize..1000)
) {
let mut channel: Channel<i32> = Channel::new(id);
channel.start_utterance(user_data, total_frames);
prop_assert!(channel.is_ready());
prop_assert_eq!(channel.state(), ChannelState::Ready);
prop_assert_eq!(channel.user_data(), Some(&user_data));
prop_assert_eq!(channel.total_frames(), total_frames);
prop_assert_eq!(channel.frame_index(), 0);
}
#[test]
fn channel_assign_lane_active(id in 0usize..100, lane in 0usize..50) {
let mut channel: Channel<i32> = Channel::new(id);
channel.start_utterance(42, None);
channel.assign_lane(lane);
prop_assert!(channel.is_active());
prop_assert_eq!(channel.lane(), Some(lane));
}
#[test]
fn channel_advance_frame(
num_advances in 1usize..20,
token_count in 0usize..1000
) {
let mut channel: Channel<i32> = Channel::new(0);
channel.start_utterance(42, None);
channel.assign_lane(0);
for i in 0..num_advances {
prop_assert_eq!(channel.frame_index(), i);
channel.advance_frame(token_count);
}
prop_assert_eq!(channel.frame_index(), num_advances);
prop_assert_eq!(channel.token_count(), token_count);
}
#[test]
fn channel_complete_returns_data(user_data in any::<i32>()) {
let mut channel: Channel<i32> = Channel::new(0);
channel.start_utterance(user_data, None);
channel.assign_lane(0);
let returned = channel.complete();
prop_assert_eq!(returned, Some(user_data));
prop_assert_eq!(channel.state(), ChannelState::Complete);
prop_assert!(channel.lane().is_none());
}
#[test]
fn channel_reset_to_idle(user_data in any::<i32>()) {
let mut channel: Channel<i32> = Channel::new(0);
channel.start_utterance(user_data, Some(100));
channel.assign_lane(5);
channel.advance_frame(50);
channel.reset();
prop_assert!(channel.is_idle());
prop_assert!(channel.lane().is_none());
prop_assert!(channel.user_data().is_none());
prop_assert_eq!(channel.frame_index(), 0);
prop_assert!(channel.total_frames().is_none());
}
#[test]
fn channel_release_lane_waiting(lane in 0usize..50) {
let mut channel: Channel<i32> = Channel::new(0);
channel.start_utterance(42, None);
channel.assign_lane(lane);
prop_assert!(channel.is_active());
channel.release_lane();
prop_assert_eq!(channel.state(), ChannelState::Waiting);
prop_assert!(channel.lane().is_none());
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn lane_new_is_available(id in 0usize..100, max_tokens in 1usize..10000) {
let lane = Lane::new(id, max_tokens);
prop_assert!(lane.is_available());
prop_assert_eq!(lane.id(), id);
prop_assert_eq!(lane.max_tokens(), max_tokens);
prop_assert!(lane.channel().is_none());
prop_assert_eq!(lane.token_count(), 0);
}
#[test]
fn lane_assign_channel_active(id in 0usize..100, channel in 0usize..50) {
let mut lane = Lane::new(id, 1000);
lane.assign_channel(channel);
prop_assert!(lane.is_active());
prop_assert_eq!(lane.channel(), Some(channel));
}
#[test]
fn lane_update_tokens_capped(max_tokens in 100usize..1000, token_count in 0usize..2000) {
let mut lane = Lane::new(0, max_tokens);
lane.assign_channel(0);
lane.update_tokens(token_count);
prop_assert!(lane.token_count() <= max_tokens);
prop_assert_eq!(lane.token_count(), token_count.min(max_tokens));
}
#[test]
fn lane_release_channel_available(channel in 0usize..100) {
let mut lane = Lane::new(0, 1000);
lane.assign_channel(channel);
lane.update_tokens(500);
let returned = lane.release_channel();
prop_assert_eq!(returned, Some(channel));
prop_assert!(lane.is_available());
prop_assert!(lane.channel().is_none());
prop_assert_eq!(lane.token_count(), 0);
}
#[test]
fn lane_frame_complete_state(channel in 0usize..50) {
let mut lane = Lane::new(0, 1000);
lane.assign_channel(channel);
lane.frame_complete();
prop_assert_eq!(lane.state(), LaneState::FrameComplete);
}
#[test]
fn lane_continue_decoding(channel in 0usize..50) {
let mut lane = Lane::new(0, 1000);
lane.assign_channel(channel);
lane.frame_complete();
prop_assert_eq!(lane.state(), LaneState::FrameComplete);
lane.continue_decoding();
prop_assert!(lane.is_active());
}
#[test]
fn lane_utterance_complete_state(channel in 0usize..50) {
let mut lane = Lane::new(0, 1000);
lane.assign_channel(channel);
lane.utterance_complete();
prop_assert_eq!(lane.state(), LaneState::UtteranceComplete);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(50))]
#[test]
fn config_preserves_settings(
max_channels in 1usize..1000,
max_lanes in 1usize..100,
max_tokens in 100usize..10000,
beam_width in 1.0f32..50.0
) {
let config = DecoderConfig {
max_channels,
max_lanes,
max_tokens,
beam_width,
};
prop_assert_eq!(config.max_channels, max_channels);
prop_assert_eq!(config.max_lanes, max_lanes);
prop_assert_eq!(config.max_tokens, max_tokens);
prop_assert!((config.beam_width - beam_width).abs() < 1e-6);
}
#[test]
fn config_estimate_memory_positive(
max_channels in 1usize..100,
max_lanes in 1usize..50,
max_tokens in 100usize..5000
) {
let config = DecoderConfig {
max_channels,
max_lanes,
max_tokens,
beam_width: 10.0,
};
let mem = config.estimate_memory();
prop_assert!(mem > 0);
let config2 = DecoderConfig {
max_channels: max_channels * 2,
max_lanes: max_lanes * 2,
max_tokens: max_tokens * 2,
beam_width: 10.0,
};
let mem2 = config2.estimate_memory();
prop_assert!(mem2 > mem);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(50))]
#[test]
fn decoder_correct_counts(
max_channels in 1usize..20,
max_lanes in 1usize..10
) {
let config = DecoderConfig {
max_channels,
max_lanes,
max_tokens: 100,
beam_width: 10.0,
};
let decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
for i in 0..max_channels {
prop_assert!(decoder.channel(i).is_some());
prop_assert!(decoder.channel(i).expect("gpu/channels.rs: required value was None/Err").is_idle());
}
for i in 0..max_lanes {
prop_assert!(decoder.lane(i).is_some());
prop_assert!(decoder.lane(i).expect("gpu/channels.rs: required value was None/Err").is_available());
}
}
#[test]
fn decoder_start_utterance_queues(num_utterances in 1usize..10) {
let config = DecoderConfig {
max_channels: 20,
max_lanes: 5,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
let mut started = Vec::new();
for i in 0..num_utterances {
if let Some(channel_id) = decoder.start_utterance(i as i32, None) {
started.push(channel_id);
}
}
prop_assert_eq!(started.len(), num_utterances);
let stats = decoder.stats();
prop_assert_eq!(stats.ready_channels, num_utterances);
}
#[test]
fn decoder_schedule_respects_lanes(
num_utterances in 5usize..15,
max_lanes in 1usize..5
) {
let config = DecoderConfig {
max_channels: 20,
max_lanes,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
for i in 0..num_utterances {
decoder.start_utterance(i as i32, None);
}
let assignments = decoder.schedule();
prop_assert!(assignments.len() <= max_lanes);
prop_assert_eq!(assignments.len(), num_utterances.min(max_lanes));
let stats = decoder.stats();
prop_assert_eq!(stats.active_lanes, assignments.len());
}
#[test]
fn decoder_active_lanes_correct(num_utterances in 1usize..5) {
let config = DecoderConfig {
max_channels: 20,
max_lanes: 10,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
for i in 0..num_utterances {
decoder.start_utterance(i as i32, None);
}
decoder.schedule();
let active = decoder.active_lanes();
prop_assert_eq!(active.len(), num_utterances);
for lane_id in active {
prop_assert!(decoder.lane(lane_id).expect("gpu/channels.rs: required value was None/Err").is_active());
}
}
#[test]
fn decoder_find_idle_channel(num_busy in 0usize..10) {
let max_channels = 15;
let config = DecoderConfig {
max_channels,
max_lanes: 10,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
for i in 0..num_busy.min(max_channels) {
decoder.start_utterance(i as i32, None);
}
decoder.schedule();
let idle = decoder.find_idle_channel();
if num_busy < max_channels {
prop_assert!(idle.is_some());
}
}
#[test]
fn decoder_complete_returns_data(user_data in proptest::collection::vec(any::<i32>(), 1..5)) {
let config = DecoderConfig {
max_channels: 20,
max_lanes: 10,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
for &data in &user_data {
decoder.start_utterance(data, None);
}
decoder.schedule();
let completed = decoder.process_frame(|_, _| (10, true));
let results = decoder.complete_utterances(&completed);
prop_assert_eq!(results.len(), user_data.len());
let returned_data: Vec<_> = results.iter().filter_map(|(_, d)| d.clone()).collect();
for data in &user_data {
prop_assert!(returned_data.contains(data));
}
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(50))]
#[test]
fn stats_lane_utilization_bounded(
num_utterances in 0usize..10,
max_lanes in 1usize..10
) {
let config = DecoderConfig {
max_channels: 20,
max_lanes,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
for i in 0..num_utterances {
decoder.start_utterance(i as i32, None);
}
decoder.schedule();
let stats = decoder.stats();
prop_assert!(stats.lane_utilization >= 0.0);
prop_assert!(stats.lane_utilization <= 1.0);
}
#[test]
fn stats_counts_consistent(num_utterances in 1usize..8, max_lanes in 1usize..5) {
let config = DecoderConfig {
max_channels: 20,
max_lanes,
max_tokens: 100,
beam_width: 10.0,
};
let mut decoder: BatchedDecoder<i32> = BatchedDecoder::new(config);
for i in 0..num_utterances {
decoder.start_utterance(i as i32, None);
}
decoder.schedule();
let stats = decoder.stats();
prop_assert_eq!(stats.active_channels + stats.ready_channels, num_utterances);
prop_assert_eq!(stats.active_lanes, stats.active_channels);
}
}
}