nam_rs/models/convnet/
block.rs1use crate::common::diagnostics::NamErrorCode;
11use crate::math::common::{AlignedVec, SimdMath};
12use crate::models::a2::activations::ActivationType;
13use crate::models::wavenet::Conv1dDyn;
14use crate::models::wavenet::common::{WAVENET_MAX_NUM_FRAMES, WaveNetLayerState};
15
16use super::batch_norm::BatchNorm1D;
17
18#[derive(Clone)]
27#[repr(align(64))]
28pub struct ConvNetBlock {
29 pub conv: Conv1dDyn,
31 pub bn: BatchNorm1D,
33 pub activation: ActivationType,
35 pub state: WaveNetLayerState,
37 scratch: AlignedVec<f32>,
39}
40
41impl ConvNetBlock {
42 pub fn new(
47 in_ch: usize,
48 out_ch: usize,
49 kernel: usize,
50 dilation: usize,
51 do_bias: bool,
52 activation: ActivationType,
53 alloc_num: usize,
54 ) -> std::io::Result<Self> {
55 let num_blocks = out_ch.div_ceil(4);
56 let weights_len = num_blocks * kernel * in_ch * 4;
57
58 let conv = Conv1dDyn {
59 weights: AlignedVec::new(weights_len, 0.0f32).map_err(|e| {
60 std::io::Error::new(std::io::ErrorKind::OutOfMemory, format!("{e}"))
61 })?,
62 bias: AlignedVec::new(out_ch, 0.0f32).map_err(|e| {
63 std::io::Error::new(std::io::ErrorKind::OutOfMemory, format!("{e}"))
64 })?,
65 do_bias,
66 dilation,
67 in_ch,
68 out_ch,
69 num_blocks,
70 interleave_width: 4,
71 kernel,
72 };
73
74 let bn = BatchNorm1D::from_fused(out_ch, &vec![0.0f32; out_ch], &vec![0.0f32; out_ch])
75 .map_err(|e| std::io::Error::new(std::io::ErrorKind::OutOfMemory, format!("{e}")))?;
76
77 let receptive_field = (kernel - 1) * dilation;
78 let state = WaveNetLayerState::new(in_ch, receptive_field, alloc_num)?;
79
80 let scratch = AlignedVec::new(out_ch * WAVENET_MAX_NUM_FRAMES, 0.0f32)
81 .map_err(|e| std::io::Error::new(std::io::ErrorKind::OutOfMemory, format!("{e}")))?;
82
83 Ok(Self {
84 conv,
85 bn,
86 activation,
87 state,
88 scratch,
89 })
90 }
91
92 pub fn receptive_field(&self) -> usize {
94 (self.conv.kernel - 1) * self.conv.dilation
95 }
96
97 pub fn set_conv_weights(&mut self, weights: &[f32]) {
99 let len = self.conv.weights.len().min(weights.len());
100 self.conv.weights[..len].copy_from_slice(&weights[..len]);
101 }
102
103 pub fn set_conv_bias(&mut self, bias: &[f32]) {
105 let len = self.conv.bias.len().min(bias.len());
106 self.conv.bias[..len].copy_from_slice(&bias[..len]);
107 }
108
109 pub fn set_bn_params(&mut self, scale: &[f32], offset: &[f32]) -> Result<(), NamErrorCode> {
111 self.bn = BatchNorm1D::from_fused(self.conv.out_ch, scale, offset)?;
112 Ok(())
113 }
114
115 #[inline(always)]
121 pub unsafe fn process_block(&mut self, input: &[f32], output: &mut [f32], num_frames: usize) {
122 unsafe {
123 crate::math::common::dispatch_simd!(
124 self,
125 process_block_internal,
126 input,
127 output,
128 num_frames
129 )
130 };
131 }
132
133 #[inline(always)]
139 pub unsafe fn process_block_internal<M: SimdMath>(
140 &mut self,
141 input: &[f32],
142 output: &mut [f32],
143 num_frames: usize,
144 ) {
145 let in_ch = self.conv.in_ch;
146 let out_ch = self.conv.out_ch;
147 let input_len = num_frames * in_ch;
148
149 let buf_start = self.state.buffer_start * in_ch;
150 self.state.layer_buffer[buf_start..buf_start + input_len]
151 .copy_from_slice(&input[..input_len]);
152
153 let scratch_slice = &mut self.scratch[..num_frames * out_ch];
154 unsafe {
155 self.conv.process_block::<M>(
156 &self.state.layer_buffer,
157 scratch_slice,
158 self.state.buffer_start,
159 num_frames,
160 None,
161 );
162 }
163
164 unsafe {
165 self.bn.process_simd::<M>(scratch_slice, num_frames);
166 }
167
168 unsafe {
169 self.activation.apply_simd::<M>(scratch_slice);
170 }
171
172 output[..num_frames * out_ch].copy_from_slice(scratch_slice);
173
174 self.state.advance_frames(num_frames, in_ch);
175 }
176
177 #[cold]
179 pub fn prewarm(&mut self) {
180 unsafe {
181 crate::math::common::dispatch_simd!(self, prewarm_internal);
182 }
183 }
184
185 #[inline(always)]
192 pub unsafe fn prewarm_internal<M: SimdMath>(&mut self) {
193 let in_ch = self.conv.in_ch;
194 let out_ch = self.conv.out_ch;
195 let kernel = self.conv.kernel;
196 let dilation = self.conv.dilation;
197
198 let silence = vec![0.0f32; in_ch];
199 let buf_start = self.state.buffer_start * in_ch;
200
201 self.state.layer_buffer[buf_start..buf_start + in_ch].copy_from_slice(&silence);
202
203 let start_idx = buf_start;
204 let src_range = start_idx..start_idx + in_ch;
205 let max_offset = (kernel - 1) * dilation + 1;
206 for offset in 1..=max_offset {
207 let dst_idx = (self.state.buffer_start - offset) * in_ch;
208 self.state
209 .layer_buffer
210 .copy_within(src_range.clone(), dst_idx);
211 }
212
213 let scratch_slice = &mut self.scratch[..out_ch];
214 unsafe {
215 self.conv.process_single_frame::<M>(
216 &self.state.layer_buffer,
217 scratch_slice,
218 self.state.buffer_start,
219 None,
220 );
221 }
222 unsafe {
223 self.bn.process_simd::<M>(scratch_slice, 1);
224 }
225 unsafe {
226 self.activation.apply_simd::<M>(scratch_slice);
227 }
228
229 self.state.advance_frames(1, in_ch);
230 }
231}
232
233#[cfg(test)]
234#[path = "convnet_block_test.rs"]
235mod tests;