#[cfg(feature = "_cuda-libraries")]
use super::{ActivationForm, BlockForm, dispatch::Plan, trunk::ConvLayer};
use super::{EmbeddingBatch, ResNetEmbedding, SharedEmbeddingActivations};
#[cfg(feature = "_cuda-libraries")]
use super::{EmbeddingTapFn, SPEAKERS_PER_CHUNK};
#[cfg(feature = "_cuda-libraries")]
use crate::inference::cuda::candidate::{WideconvAlgorithm, WideconvFp16Tiles};
use crate::inference::cuda::{CudaError, CudaRuntime};
use cudarc::driver::CudaSlice;
use std::sync::Arc;
#[cfg(feature = "_cuda-libraries")]
impl EmbeddingBatch {
pub fn forward_with_taps(
&mut self,
runtime: &CudaRuntime,
tap: &mut EmbeddingTapFn<'_>,
) -> Result<(), CudaError> {
let activations = self.activations.clone();
let mut storage = activations.lock()?;
self.run_with_activations(
runtime,
super::PlanSet::Selected,
super::ActivationForm::Fp32,
tap,
storage.buffers_mut(),
)
}
pub fn embed(
&mut self,
runtime: &CudaRuntime,
fbank: &[f32],
masks: &[f32],
) -> Result<Vec<f32>, CudaError> {
let stream = runtime.stream();
self.fbank.copy_from_host(stream, fbank)?;
self.masks.copy_from_host(stream, masks)?;
self.forward(runtime)?;
self.download_output(runtime)
}
pub fn rows(&self) -> usize {
self.chunks * SPEAKERS_PER_CHUNK
}
pub fn plan_count(&self) -> usize {
self.plans.len()
}
pub fn half_hidden_blocks(&self) -> (usize, usize) {
let (fp16_pairs, _, half_pairs, _) = self.half_blocks();
(fp16_pairs, half_pairs)
}
pub fn half_output_blocks(&self) -> (usize, usize) {
let (_, fp16_outputs, _, half_outputs) = self.half_blocks();
(fp16_outputs, half_outputs)
}
fn half_blocks(&self) -> (usize, usize, usize, usize) {
let fp16 = |layer: &ConvLayer| {
let conv = layer.conv(self.chunks, self.model.math);
let tiles: &[WideconvFp16Tiles] = match conv.in_channels {
32 | 64 => &[WideconvFp16Tiles::Wide],
128 | 256 => &[WideconvFp16Tiles::Wide, WideconvFp16Tiles::Narrow],
_ => return false,
};
conv.out_channels == conv.in_channels
&& conv.stride == [1, 1]
&& self.plans.iter().any(|(name, plan)| {
name == layer.name()
&& matches!(plan, Plan::Wideconv(plan)
if tiles.iter().any(|&tiles| {
plan.config().algorithm == WideconvAlgorithm::Fp16(tiles)
}))
})
};
let blocks = &self.model.trunk.blocks;
let fp16_pairs = blocks
.iter()
.filter(|block| fp16(&block.conv1) && fp16(&block.conv2))
.count();
let fp16_outputs = blocks
.windows(2)
.filter(|pair| {
let [block, next] = pair else { return false };
next.shortcut.is_none()
&& fp16(&block.conv2)
&& fp16(&next.conv1)
&& fp16(&next.conv2)
})
.count();
let forms = BlockForm::plan(blocks, &self.plans, ActivationForm::Half);
let half_pairs = forms.iter().filter(|form| form.hidden).count();
let half_outputs = forms.iter().filter(|form| form.output).count();
(fp16_pairs, fp16_outputs, half_pairs, half_outputs)
}
pub fn has_graph(&self) -> bool {
self.graph.is_some()
}
pub fn workspace_bytes(&self) -> usize {
self.workspace.len()
}
pub fn buffer_bytes(&self) -> usize {
let floats = self.fbank.len()
+ self.masks.len()
+ self.stem_input.len()
+ self.pooled.len()
+ self.output.len();
floats * size_of::<f32>() + self.activations.retained_bytes()
}
}
impl ResNetEmbedding {
pub fn batch(&self, runtime: &CudaRuntime, chunks: usize) -> Result<EmbeddingBatch, CudaError> {
let activations = self.activations(runtime, chunks)?;
self.batch_with_activations(runtime, chunks, activations)
}
}
impl SharedEmbeddingActivations {
pub(crate) fn retained_bytes(&self) -> usize {
let storage = self.lock().unwrap();
let buffers = storage.buffers();
(buffers.trunk.iter().map(CudaSlice::len).sum::<usize>()
+ buffers.hidden.len()
+ buffers.shortcut.len())
* size_of::<f32>()
}
}
impl EmbeddingBatch {
pub(crate) fn shares_activations(&self, other: &SharedEmbeddingActivations) -> bool {
Arc::ptr_eq(&self.activations.0, &other.0)
}
}