use std::collections::HashMap;
use std::time::{Duration, Instant};
use voirs_acoustic::{
AcousticModel, DummyAcousticModel, Phoneme as AcousticPhoneme,
SynthesisConfig as AcousticConfig,
};
use voirs_g2p::{DummyG2p, G2p, LanguageCode};
use voirs_vocoder::{
DummyVocoder, MelSpectrogram as VocoderMel, SynthesisConfig as VocoderConfig,
Vocoder,
};
pub struct PerformanceBenchmarks {
target_rtf: f64,
warmup_iterations: usize,
measurement_iterations: usize,
}
impl PerformanceBenchmarks {
pub fn new() -> Self {
Self {
target_rtf: 0.5, warmup_iterations: 3,
measurement_iterations: 10,
}
}
pub async fn run_benchmarks(&self) -> Result<BenchmarkResults, Box<dyn std::error::Error>> {
println!("⚡ Running Performance Benchmarks");
println!("================================");
println!(
"Target RTF: {} ({}x real-time)",
self.target_rtf,
1.0 / self.target_rtf
);
println!("Warmup iterations: {}", self.warmup_iterations);
println!("Measurement iterations: {}", self.measurement_iterations);
let mut results = BenchmarkResults::new();
results.g2p_benchmarks = self.benchmark_g2p().await?;
results.acoustic_benchmarks = self.benchmark_acoustic_model().await?;
results.vocoder_benchmarks = self.benchmark_vocoder().await?;
results.pipeline_benchmarks = self.benchmark_full_pipeline().await?;
results.memory_benchmarks = self.benchmark_memory_usage().await?;
results.throughput_benchmarks = self.benchmark_throughput().await?;
println!("\n✅ Performance Benchmarks Complete");
results.print_summary();
Ok(results)
}
async fn benchmark_g2p(&self) -> Result<G2pBenchmarks, Box<dyn std::error::Error>> {
println!("\n📝 Benchmarking G2P Performance");
println!("------------------------------");
let mut benchmarks = G2pBenchmarks::new();
let g2p = DummyG2p::new();
let repetitive_text = "a".repeat(100);
let test_cases = vec![
("hello", "simple"),
("hello world", "phrase"),
("The quick brown fox jumps over the lazy dog", "sentence"),
("This is a longer sentence with more complex words like synthesis and phonological transformation", "complex"),
(&repetitive_text, "repetitive"), ];
for (text, category) in test_cases {
let display_text = if text.len() > 50 {
text[..47].to_string() + "..."
} else {
text.to_string()
};
println!(" Testing {}: \"{}\"", category, display_text);
for _ in 0..self.warmup_iterations {
let _ = g2p.to_phonemes(text, Some(LanguageCode::EnUs)).await?;
}
let mut durations = Vec::new();
let mut phoneme_counts = Vec::new();
for _ in 0..self.measurement_iterations {
let start = Instant::now();
let phonemes = g2p.to_phonemes(text, Some(LanguageCode::EnUs)).await?;
let duration = start.elapsed();
durations.push(duration);
phoneme_counts.push(phonemes.len());
}
let avg_duration = durations.iter().sum::<Duration>() / durations.len() as u32;
let avg_phonemes = phoneme_counts.iter().sum::<usize>() / phoneme_counts.len();
let phonemes_per_sec = if avg_duration.as_secs_f64() > 0.0 {
avg_phonemes as f64 / avg_duration.as_secs_f64()
} else {
0.0
};
let benchmark = G2pBenchmark {
category: category.to_string(),
text_length: text.len(),
avg_duration,
avg_phonemes,
phonemes_per_sec,
min_duration: *durations.iter().min().unwrap(),
max_duration: *durations.iter().max().unwrap(),
};
println!(
" Duration: {:?} | Phonemes: {} | Rate: {:.1} phonemes/sec",
avg_duration, avg_phonemes, phonemes_per_sec
);
benchmarks.results.insert(category.to_string(), benchmark);
}
Ok(benchmarks)
}
async fn benchmark_acoustic_model(
&self,
) -> Result<AcousticBenchmarks, Box<dyn std::error::Error>> {
println!("\n🎵 Benchmarking Acoustic Model Performance");
println!("----------------------------------------");
let mut benchmarks = AcousticBenchmarks::new();
let acoustic_model = DummyAcousticModel::new();
let test_cases = vec![
(vec!["h", "ə", "l", "oʊ"], "short"),
(
vec!["h", "ə", "l", "oʊ", "w", "ɜː", "r", "l", "d"],
"medium",
),
(
vec![
"ð", "ə", "k", "w", "ɪ", "k", "b", "r", "aʊ", "n", "f", "ɑ", "k", "s", "ʤ",
"ʌ", "m", "p", "s", "oʊ", "v", "ər", "ð", "ə", "l", "eɪ", "z", "i", "d", "ɔ",
"g",
],
"long",
),
];
for (phoneme_strs, category) in test_cases {
println!(" Testing {} ({} phonemes)", category, phoneme_strs.len());
let phonemes: Vec<AcousticPhoneme> = phoneme_strs
.iter()
.map(|s| AcousticPhoneme::new(*s))
.collect();
let config = AcousticConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
emotion: None,
voice_style: None,
};
for _ in 0..self.warmup_iterations {
let _ = acoustic_model.synthesize(&phonemes, Some(&config)).await?;
}
let mut durations = Vec::new();
let mut mel_specs = Vec::new();
for _ in 0..self.measurement_iterations {
let start = Instant::now();
let mel = acoustic_model.synthesize(&phonemes, Some(&config)).await?;
let duration = start.elapsed();
durations.push(duration);
mel_specs.push(mel);
}
let avg_duration = durations.iter().sum::<Duration>() / durations.len() as u32;
let avg_mel = &mel_specs[0]; let audio_duration = avg_mel.duration();
let rtf = if audio_duration > 0.0 {
avg_duration.as_secs_f64() / audio_duration as f64
} else {
f64::INFINITY
};
let benchmark = AcousticBenchmark {
category: category.to_string(),
phoneme_count: phonemes.len(),
avg_duration,
audio_duration,
rtf,
mel_dimensions: (avg_mel.n_mels, avg_mel.n_frames),
min_duration: *durations.iter().min().unwrap(),
max_duration: *durations.iter().max().unwrap(),
meets_realtime: rtf < 1.0,
};
println!(
" Duration: {:?} | Audio: {:.2}s | RTF: {:.3} | Mel: {}x{}",
avg_duration, audio_duration, rtf, avg_mel.n_mels, avg_mel.n_frames
);
if rtf < self.target_rtf {
println!(" ✅ Meets target RTF ({:.2})", self.target_rtf);
} else if rtf < 1.0 {
println!(" ⚠️ Real-time but above target RTF");
} else {
println!(" ❌ Slower than real-time");
}
benchmarks.results.insert(category.to_string(), benchmark);
}
Ok(benchmarks)
}
async fn benchmark_vocoder(&self) -> Result<VocoderBenchmarks, Box<dyn std::error::Error>> {
println!("\n🔊 Benchmarking Vocoder Performance");
println!("----------------------------------");
let mut benchmarks = VocoderBenchmarks::new();
let vocoder = DummyVocoder::new();
let test_cases = vec![
((80, 50), "short"), ((80, 100), "medium"), ((80, 200), "long"), ((80, 500), "extended"), ];
for ((n_mels, n_frames), category) in test_cases {
println!(" Testing {} ({}x{} mel)", category, n_mels, n_frames);
let mel_data = vec![vec![0.5f32; n_frames]; n_mels];
let mel = VocoderMel::new(mel_data, 22050, 256);
let config = VocoderConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
};
let audio_duration = (n_frames * 256) as f64 / 22050.0;
for _ in 0..self.warmup_iterations {
let _ = vocoder.vocode(&mel, Some(&config)).await?;
}
let mut durations = Vec::new();
let mut audio_buffers = Vec::new();
for _ in 0..self.measurement_iterations {
let start = Instant::now();
let audio = vocoder.vocode(&mel, Some(&config)).await?;
let duration = start.elapsed();
durations.push(duration);
audio_buffers.push(audio);
}
let avg_duration = durations.iter().sum::<Duration>() / durations.len() as u32;
let avg_audio = &audio_buffers[0];
let rtf = if audio_duration > 0.0 {
avg_duration.as_secs_f64() / audio_duration
} else {
f64::INFINITY
};
let benchmark = VocoderBenchmark {
category: category.to_string(),
mel_dimensions: (n_mels, n_frames),
avg_duration,
audio_duration,
rtf,
audio_samples: avg_audio.len(),
min_duration: *durations.iter().min().unwrap(),
max_duration: *durations.iter().max().unwrap(),
meets_realtime: rtf < 1.0,
};
println!(
" Duration: {:?} | Audio: {:.2}s | RTF: {:.3} | Samples: {}",
avg_duration,
audio_duration,
rtf,
avg_audio.len()
);
if rtf < self.target_rtf {
println!(" ✅ Meets target RTF ({:.2})", self.target_rtf);
} else if rtf < 1.0 {
println!(" ⚠️ Real-time but above target RTF");
} else {
println!(" ❌ Slower than real-time");
}
benchmarks.results.insert(category.to_string(), benchmark);
}
Ok(benchmarks)
}
async fn benchmark_full_pipeline(
&self,
) -> Result<PipelineBenchmarks, Box<dyn std::error::Error>> {
println!("\n🔄 Benchmarking Full Pipeline Performance");
println!("---------------------------------------");
let mut benchmarks = PipelineBenchmarks::new();
let g2p = DummyG2p::new();
let acoustic_model = DummyAcousticModel::new();
let vocoder = DummyVocoder::new();
let test_cases = vec![
("hello", "word"),
("hello world", "phrase"),
("The quick brown fox", "sentence"),
(
"Text-to-speech synthesis is the artificial production of human speech",
"paragraph",
),
];
for (text, category) in test_cases {
println!(" Testing {}: \"{}\"", category, text);
let acoustic_config = AcousticConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
emotion: None,
voice_style: None,
};
let vocoder_config = VocoderConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
};
for _ in 0..self.warmup_iterations {
let phonemes = g2p.to_phonemes(text, Some(LanguageCode::EnUs)).await?;
let acoustic_phonemes: Vec<AcousticPhoneme> = phonemes
.iter()
.map(|p| AcousticPhoneme::new(&p.symbol))
.collect();
let mel = acoustic_model
.synthesize(&acoustic_phonemes, Some(&acoustic_config))
.await?;
let vocoder_mel =
VocoderMel::new(mel.data.clone(), mel.sample_rate, mel.hop_length);
let _ = vocoder.vocode(&vocoder_mel, Some(&vocoder_config)).await?;
}
let mut total_durations = Vec::new();
let mut g2p_durations = Vec::new();
let mut acoustic_durations = Vec::new();
let mut vocoder_durations = Vec::new();
let mut audio_durations = Vec::new();
for _ in 0..self.measurement_iterations {
let total_start = Instant::now();
let g2p_start = Instant::now();
let phonemes = g2p.to_phonemes(text, Some(LanguageCode::EnUs)).await?;
let g2p_duration = g2p_start.elapsed();
let acoustic_start = Instant::now();
let acoustic_phonemes: Vec<AcousticPhoneme> = phonemes
.iter()
.map(|p| AcousticPhoneme::new(&p.symbol))
.collect();
let mel = acoustic_model
.synthesize(&acoustic_phonemes, Some(&acoustic_config))
.await?;
let acoustic_duration = acoustic_start.elapsed();
let vocoder_start = Instant::now();
let vocoder_mel =
VocoderMel::new(mel.data.clone(), mel.sample_rate, mel.hop_length);
let audio = vocoder.vocode(&vocoder_mel, Some(&vocoder_config)).await?;
let vocoder_duration = vocoder_start.elapsed();
let total_duration = total_start.elapsed();
let audio_duration = audio.len() as f64 / audio.sample_rate() as f64;
total_durations.push(total_duration);
g2p_durations.push(g2p_duration);
acoustic_durations.push(acoustic_duration);
vocoder_durations.push(vocoder_duration);
audio_durations.push(audio_duration);
}
let avg_total = total_durations.iter().sum::<Duration>() / total_durations.len() as u32;
let avg_g2p = g2p_durations.iter().sum::<Duration>() / g2p_durations.len() as u32;
let avg_acoustic =
acoustic_durations.iter().sum::<Duration>() / acoustic_durations.len() as u32;
let avg_vocoder =
vocoder_durations.iter().sum::<Duration>() / vocoder_durations.len() as u32;
let avg_audio_duration =
audio_durations.iter().sum::<f64>() / audio_durations.len() as f64;
let total_rtf = if avg_audio_duration > 0.0 {
avg_total.as_secs_f64() / avg_audio_duration
} else {
f64::INFINITY
};
let benchmark = PipelineBenchmark {
category: category.to_string(),
text_length: text.len(),
total_duration: avg_total,
g2p_duration: avg_g2p,
acoustic_duration: avg_acoustic,
vocoder_duration: avg_vocoder,
audio_duration: avg_audio_duration,
total_rtf,
meets_realtime: total_rtf < 1.0,
meets_target: total_rtf < self.target_rtf,
};
println!(
" Total: {:?} | G2P: {:?} | Acoustic: {:?} | Vocoder: {:?}",
avg_total, avg_g2p, avg_acoustic, avg_vocoder
);
println!(
" Audio: {:.2}s | RTF: {:.3}",
avg_audio_duration, total_rtf
);
if total_rtf < self.target_rtf {
println!(" ✅ Meets target RTF ({:.2})", self.target_rtf);
} else if total_rtf < 1.0 {
println!(" ⚠️ Real-time but above target RTF");
} else {
println!(" ❌ Slower than real-time");
}
benchmarks.results.insert(category.to_string(), benchmark);
}
Ok(benchmarks)
}
async fn benchmark_memory_usage(&self) -> Result<MemoryBenchmarks, Box<dyn std::error::Error>> {
println!("\n💾 Benchmarking Memory Usage");
println!("---------------------------");
let mut benchmarks = MemoryBenchmarks::new();
let large_text = "This is a longer text for testing memory usage. ".repeat(10);
let test_cases = vec![
("small", "hello"),
("medium", "The quick brown fox jumps over the lazy dog"),
("large", &large_text),
];
for (category, text) in test_cases {
println!(" Testing {} workload", category);
let initial_memory = self.get_memory_usage();
let g2p = DummyG2p::new();
let acoustic_model = DummyAcousticModel::new();
let vocoder = DummyVocoder::new();
let after_init_memory = self.get_memory_usage();
let mut peak_memory = after_init_memory;
for i in 0..10 {
let phonemes = g2p.to_phonemes(text, Some(LanguageCode::EnUs)).await?;
let acoustic_phonemes: Vec<AcousticPhoneme> = phonemes
.iter()
.map(|p| AcousticPhoneme::new(&p.symbol))
.collect();
let config = AcousticConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
emotion: None,
voice_style: None,
};
let mel = acoustic_model
.synthesize(&acoustic_phonemes, Some(&config))
.await?;
let vocoder_mel =
VocoderMel::new(mel.data.clone(), mel.sample_rate, mel.hop_length);
let vocoder_config = VocoderConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
};
let _audio = vocoder.vocode(&vocoder_mel, Some(&vocoder_config)).await?;
let current_memory = self.get_memory_usage();
if current_memory > peak_memory {
peak_memory = current_memory;
}
if i % 3 == 0 {
tokio::task::yield_now().await;
}
}
let final_memory = self.get_memory_usage();
let benchmark = MemoryBenchmark {
category: category.to_string(),
initial_memory,
after_init_memory,
peak_memory,
final_memory,
memory_growth: final_memory - initial_memory,
peak_growth: peak_memory - initial_memory,
};
println!(
" Initial: {} KB | After init: {} KB | Peak: {} KB | Final: {} KB",
initial_memory / 1024,
after_init_memory / 1024,
peak_memory / 1024,
final_memory / 1024
);
println!(
" Growth: {} KB | Peak growth: {} KB",
benchmark.memory_growth / 1024,
benchmark.peak_growth / 1024
);
benchmarks.results.insert(category.to_string(), benchmark);
}
Ok(benchmarks)
}
async fn benchmark_throughput(
&self,
) -> Result<ThroughputBenchmarks, Box<dyn std::error::Error>> {
println!("\n🚀 Benchmarking Throughput");
println!("-------------------------");
let mut benchmarks = ThroughputBenchmarks::new();
let test_cases = vec![(1, "sequential"), (2, "dual"), (4, "quad"), (8, "octa")];
let test_text = "performance test";
for (concurrency, category) in test_cases {
println!(
" Testing {} processing (concurrency: {})",
category, concurrency
);
let start_time = Instant::now();
let iterations = 20;
let mut handles = Vec::new();
let iterations_per_task = iterations / concurrency;
for _ in 0..concurrency {
let text = test_text.to_string();
let handle = tokio::spawn(async move {
let g2p = DummyG2p::new();
let acoustic_model = DummyAcousticModel::new();
let vocoder = DummyVocoder::new();
let mut task_durations = Vec::new();
for _ in 0..iterations_per_task {
let task_start = Instant::now();
let phonemes = g2p
.to_phonemes(&text, Some(LanguageCode::EnUs))
.await
.unwrap();
let acoustic_phonemes: Vec<AcousticPhoneme> = phonemes
.iter()
.map(|p| AcousticPhoneme::new(&p.symbol))
.collect();
let acoustic_config = AcousticConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
emotion: None,
voice_style: None,
};
let mel = acoustic_model
.synthesize(&acoustic_phonemes, Some(&acoustic_config))
.await
.unwrap();
let vocoder_mel =
VocoderMel::new(mel.data.clone(), mel.sample_rate, mel.hop_length);
let vocoder_config = VocoderConfig {
speed: 1.0,
pitch_shift: 0.0,
energy: 1.0,
speaker_id: None,
seed: Some(42),
};
let _audio = vocoder
.vocode(&vocoder_mel, Some(&vocoder_config))
.await
.unwrap();
task_durations.push(task_start.elapsed());
}
task_durations
});
handles.push(handle);
}
let mut all_durations = Vec::new();
for handle in handles {
let task_durations = handle.await?;
all_durations.extend(task_durations);
}
let total_time = start_time.elapsed();
let avg_per_request =
all_durations.iter().sum::<Duration>() / all_durations.len() as u32;
let requests_per_second = iterations as f64 / total_time.as_secs_f64();
let benchmark = ThroughputBenchmark {
category: category.to_string(),
concurrency,
total_requests: iterations,
total_time,
avg_per_request,
requests_per_second,
min_request_time: *all_durations.iter().min().unwrap(),
max_request_time: *all_durations.iter().max().unwrap(),
};
println!(
" Total time: {:?} | Avg per request: {:?}",
total_time, avg_per_request
);
println!(
" Requests/sec: {:.2} | Min: {:?} | Max: {:?}",
requests_per_second, benchmark.min_request_time, benchmark.max_request_time
);
benchmarks.results.insert(category.to_string(), benchmark);
}
Ok(benchmarks)
}
fn get_memory_usage(&self) -> usize {
1024 * 1024 }
}
#[derive(Debug)]
pub struct BenchmarkResults {
pub g2p_benchmarks: G2pBenchmarks,
pub acoustic_benchmarks: AcousticBenchmarks,
pub vocoder_benchmarks: VocoderBenchmarks,
pub pipeline_benchmarks: PipelineBenchmarks,
pub memory_benchmarks: MemoryBenchmarks,
pub throughput_benchmarks: ThroughputBenchmarks,
}
impl BenchmarkResults {
pub fn new() -> Self {
Self {
g2p_benchmarks: G2pBenchmarks::new(),
acoustic_benchmarks: AcousticBenchmarks::new(),
vocoder_benchmarks: VocoderBenchmarks::new(),
pipeline_benchmarks: PipelineBenchmarks::new(),
memory_benchmarks: MemoryBenchmarks::new(),
throughput_benchmarks: ThroughputBenchmarks::new(),
}
}
pub fn print_summary(&self) {
println!("\n📊 Performance Benchmark Summary");
println!("===============================");
let g2p_avg_rate: f64 = self
.g2p_benchmarks
.results
.values()
.map(|b| b.phonemes_per_sec)
.sum::<f64>()
/ self.g2p_benchmarks.results.len() as f64;
println!("G2P Performance:");
println!(" - Average phonemes/sec: {:.1}", g2p_avg_rate);
let acoustic_rtfs: Vec<f64> = self
.acoustic_benchmarks
.results
.values()
.map(|b| b.rtf)
.collect();
let acoustic_avg_rtf = acoustic_rtfs.iter().sum::<f64>() / acoustic_rtfs.len() as f64;
let acoustic_realtime_count = self
.acoustic_benchmarks
.results
.values()
.filter(|b| b.meets_realtime)
.count();
println!("Acoustic Model Performance:");
println!(" - Average RTF: {:.3}", acoustic_avg_rtf);
println!(
" - Real-time capable: {}/{}",
acoustic_realtime_count,
self.acoustic_benchmarks.results.len()
);
let vocoder_rtfs: Vec<f64> = self
.vocoder_benchmarks
.results
.values()
.map(|b| b.rtf)
.collect();
let vocoder_avg_rtf = vocoder_rtfs.iter().sum::<f64>() / vocoder_rtfs.len() as f64;
let vocoder_realtime_count = self
.vocoder_benchmarks
.results
.values()
.filter(|b| b.meets_realtime)
.count();
println!("Vocoder Performance:");
println!(" - Average RTF: {:.3}", vocoder_avg_rtf);
println!(
" - Real-time capable: {}/{}",
vocoder_realtime_count,
self.vocoder_benchmarks.results.len()
);
let pipeline_rtfs: Vec<f64> = self
.pipeline_benchmarks
.results
.values()
.map(|b| b.total_rtf)
.collect();
let pipeline_avg_rtf = pipeline_rtfs.iter().sum::<f64>() / pipeline_rtfs.len() as f64;
let pipeline_realtime_count = self
.pipeline_benchmarks
.results
.values()
.filter(|b| b.meets_realtime)
.count();
println!("Full Pipeline Performance:");
println!(" - Average RTF: {:.3}", pipeline_avg_rtf);
println!(
" - Real-time capable: {}/{}",
pipeline_realtime_count,
self.pipeline_benchmarks.results.len()
);
let max_throughput = self
.throughput_benchmarks
.results
.values()
.map(|b| b.requests_per_second)
.fold(0.0f64, f64::max);
println!("Throughput Performance:");
println!(" - Max requests/sec: {:.2}", max_throughput);
}
}
#[derive(Debug)]
pub struct G2pBenchmarks {
pub results: HashMap<String, G2pBenchmark>,
}
impl G2pBenchmarks {
pub fn new() -> Self {
Self {
results: HashMap::new(),
}
}
}
#[derive(Debug)]
pub struct G2pBenchmark {
pub category: String,
pub text_length: usize,
pub avg_duration: Duration,
pub avg_phonemes: usize,
pub phonemes_per_sec: f64,
pub min_duration: Duration,
pub max_duration: Duration,
}
#[derive(Debug)]
pub struct AcousticBenchmarks {
pub results: HashMap<String, AcousticBenchmark>,
}
impl AcousticBenchmarks {
pub fn new() -> Self {
Self {
results: HashMap::new(),
}
}
}
#[derive(Debug)]
pub struct AcousticBenchmark {
pub category: String,
pub phoneme_count: usize,
pub avg_duration: Duration,
pub audio_duration: f32,
pub rtf: f64,
pub mel_dimensions: (usize, usize),
pub min_duration: Duration,
pub max_duration: Duration,
pub meets_realtime: bool,
}
#[derive(Debug)]
pub struct VocoderBenchmarks {
pub results: HashMap<String, VocoderBenchmark>,
}
impl VocoderBenchmarks {
pub fn new() -> Self {
Self {
results: HashMap::new(),
}
}
}
#[derive(Debug)]
pub struct VocoderBenchmark {
pub category: String,
pub mel_dimensions: (usize, usize),
pub avg_duration: Duration,
pub audio_duration: f64,
pub rtf: f64,
pub audio_samples: usize,
pub min_duration: Duration,
pub max_duration: Duration,
pub meets_realtime: bool,
}
#[derive(Debug)]
pub struct PipelineBenchmarks {
pub results: HashMap<String, PipelineBenchmark>,
}
impl PipelineBenchmarks {
pub fn new() -> Self {
Self {
results: HashMap::new(),
}
}
}
#[derive(Debug)]
pub struct PipelineBenchmark {
pub category: String,
pub text_length: usize,
pub total_duration: Duration,
pub g2p_duration: Duration,
pub acoustic_duration: Duration,
pub vocoder_duration: Duration,
pub audio_duration: f64,
pub total_rtf: f64,
pub meets_realtime: bool,
pub meets_target: bool,
}
#[derive(Debug)]
pub struct MemoryBenchmarks {
pub results: HashMap<String, MemoryBenchmark>,
}
impl MemoryBenchmarks {
pub fn new() -> Self {
Self {
results: HashMap::new(),
}
}
}
#[derive(Debug)]
pub struct MemoryBenchmark {
pub category: String,
pub initial_memory: usize,
pub after_init_memory: usize,
pub peak_memory: usize,
pub final_memory: usize,
pub memory_growth: usize,
pub peak_growth: usize,
}
#[derive(Debug)]
pub struct ThroughputBenchmarks {
pub results: HashMap<String, ThroughputBenchmark>,
}
impl ThroughputBenchmarks {
pub fn new() -> Self {
Self {
results: HashMap::new(),
}
}
}
#[derive(Debug)]
pub struct ThroughputBenchmark {
pub category: String,
pub concurrency: usize,
pub total_requests: usize,
pub total_time: Duration,
pub avg_per_request: Duration,
pub requests_per_second: f64,
pub min_request_time: Duration,
pub max_request_time: Duration,
}
#[tokio::test]
async fn test_performance_benchmarks() {
let benchmark_suite = PerformanceBenchmarks::new();
let results = benchmark_suite
.run_benchmarks()
.await
.expect("Performance benchmarks failed");
assert!(
!results.g2p_benchmarks.results.is_empty(),
"G2P benchmarks should have results"
);
assert!(
!results.acoustic_benchmarks.results.is_empty(),
"Acoustic benchmarks should have results"
);
assert!(
!results.vocoder_benchmarks.results.is_empty(),
"Vocoder benchmarks should have results"
);
assert!(
!results.pipeline_benchmarks.results.is_empty(),
"Pipeline benchmarks should have results"
);
let realtime_components = results
.pipeline_benchmarks
.results
.values()
.filter(|b| b.meets_realtime)
.count();
println!(
"Real-time capable pipeline configurations: {}",
realtime_components
);
}