shape_runtime/
simd_forward_fill.rs1#[cfg(target_arch = "x86_64")]
7use std::arch::x86_64::*;
8
9#[cfg(target_arch = "x86_64")]
14#[target_feature(enable = "avx2")]
15#[allow(unused_assignments)]
16unsafe fn forward_fill_avx2(data: &mut [f64]) {
17 if data.is_empty() {
18 return;
19 }
20
21 let mut last_valid = f64::NAN;
22 let mut i = 0;
23
24 while i < data.len() && data[i].is_nan() {
26 i += 1;
27 }
28
29 if i < data.len() {
30 last_valid = data[i];
31 }
32
33 while i + 4 <= data.len() {
35 let chunk = unsafe { _mm256_loadu_pd(&data[i] as *const f64) };
36
37 let nan_mask = _mm256_cmp_pd(chunk, chunk, _CMP_UNORD_Q);
39
40 let last_valid_vec = _mm256_set1_pd(last_valid);
42
43 let result = _mm256_blendv_pd(chunk, last_valid_vec, nan_mask);
45
46 unsafe { _mm256_storeu_pd(&mut data[i] as *mut f64, result) };
48
49 for j in (i..i + 4).rev() {
51 if !data[j].is_nan() {
52 last_valid = data[j];
53 break;
54 }
55 }
56
57 i += 4;
58 }
59
60 while i < data.len() {
62 if data[i].is_nan() {
63 data[i] = last_valid;
64 } else {
65 last_valid = data[i];
66 }
67 i += 1;
68 }
69}
70
71#[cfg(target_arch = "x86_64")]
73#[target_feature(enable = "sse2")]
74#[allow(unused_assignments)]
75unsafe fn forward_fill_sse2(data: &mut [f64]) {
76 if data.is_empty() {
77 return;
78 }
79
80 let mut last_valid = f64::NAN;
81 let mut i = 0;
82
83 while i < data.len() && data[i].is_nan() {
85 i += 1;
86 }
87
88 if i < data.len() {
89 last_valid = data[i];
90 }
91
92 while i + 2 <= data.len() {
94 let chunk = unsafe { _mm_loadu_pd(&data[i] as *const f64) };
95
96 let nan_mask = _mm_cmpunord_pd(chunk, chunk);
98
99 let last_valid_vec = _mm_set1_pd(last_valid);
101
102 let result = _mm_or_pd(
104 _mm_and_pd(nan_mask, last_valid_vec),
105 _mm_andnot_pd(nan_mask, chunk),
106 );
107
108 unsafe { _mm_storeu_pd(&mut data[i] as *mut f64, result) };
110
111 for j in (i..i + 2).rev() {
113 if !data[j].is_nan() {
114 last_valid = data[j];
115 break;
116 }
117 }
118
119 i += 2;
120 }
121
122 if i < data.len() {
124 if data[i].is_nan() {
125 data[i] = last_valid;
126 } else {
127 last_valid = data[i];
128 }
129 }
130}
131
132fn forward_fill_scalar(data: &mut [f64]) {
134 if data.is_empty() {
135 return;
136 }
137
138 let mut last_valid = f64::NAN;
139
140 for value in data.iter_mut() {
141 if value.is_nan() {
142 *value = last_valid;
143 } else {
144 last_valid = *value;
145 }
146 }
147}
148
149pub fn forward_fill(data: &mut [f64]) {
154 #[cfg(target_arch = "x86_64")]
155 {
156 if is_x86_feature_detected!("avx2") {
157 unsafe { forward_fill_avx2(data) }
158 } else if is_x86_feature_detected!("sse2") {
159 unsafe { forward_fill_sse2(data) }
160 } else {
161 forward_fill_scalar(data)
162 }
163 }
164
165 #[cfg(not(target_arch = "x86_64"))]
166 {
167 forward_fill_scalar(data)
168 }
169}
170
171pub fn forward_fill_interpolate(
176 source: &[f64],
177 source_indices: &[usize],
178 target_size: usize,
179 interpolate: bool,
180) -> Vec<f64> {
181 let mut result = vec![f64::NAN; target_size];
182
183 if source.is_empty() || source_indices.is_empty() {
184 return result;
185 }
186
187 for (&idx, &value) in source_indices.iter().zip(source.iter()) {
189 if idx < target_size {
190 result[idx] = value;
191 }
192 }
193
194 if interpolate {
195 let mut prev_idx = None;
197 let mut prev_val = f64::NAN;
198
199 for (i, &idx) in source_indices.iter().enumerate() {
200 if idx >= target_size {
201 break;
202 }
203
204 let val = source[i];
205
206 if let Some(p_idx) = prev_idx {
207 let gap = idx - p_idx;
209 if gap > 1 {
210 let step = (val - prev_val) / gap as f64;
211 for j in 1..gap {
212 result[p_idx + j] = prev_val + step * j as f64;
213 }
214 }
215 }
216
217 prev_idx = Some(idx);
218 prev_val = val;
219 }
220
221 forward_fill(&mut result);
223 } else {
224 forward_fill(&mut result);
226 }
227
228 result
229}
230
231pub fn batch_forward_fill(series: &mut [Vec<f64>]) {
236 for data in series.iter_mut() {
238 forward_fill(data);
239 }
240}