1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//use crate::common::validate_inputs;
use crate::common_simd::assets::validate_inputs;
use crate::indicators::ad::{min_data, IndicatorState, INPUTS_WIDTH, OPTIONS_WIDTH};
use crate::indicators::simd_indicators::ad_simd::calc_simd;
use crate::indicators::simd_indicators::road_train::{Asset, Driver, PrimeMover};
use crate::types::IndicatorError;
use std::simd::Simd;
/// SIMD driver that advances the Accumulation/Distribution (AD) line across `N` asset lanes
/// per scheduling epoch.
struct AdDriver;
impl Driver<f64, ()> for AdDriver {
/// Processes one epoch of bars for `N` assets simultaneously using SIMD.
///
/// Reads from `inputs[asset][field]` (high, low, close, volume), writes the running AD value
/// to `outputs[asset][0]`, and updates `states[asset]` in place.
fn next_run<const N: usize>(
&mut self,
inputs: Vec<Vec<&[f64]>>,
mut outputs: Vec<Vec<&mut [f64]>>,
mut states: Vec<&mut f64>,
_options: Vec<Option<&()>>,
) {
let len = inputs[0][0].len();
// Optimization 1: Direct array construction instead of collect+try_into
let mut ads = Simd::<f64, N>::from_array(std::array::from_fn(|i| unsafe {
**states.get_unchecked(i)
}));
// Optimization 2: Pre-compute all input and output pointers
let (high_ptrs, low_ptrs, close_ptrs, volume_ptrs) =
crate::extract_input_ptrs!(inputs, N, high_ptrs, low_ptrs, close_ptrs, volume_ptrs);
let ad_line_ptr = crate::extract_output_ptrs!(outputs, N, ad_line_ptr);
// Optimization 3: Simplified main loop with pre-computed offsets
for i in 0..len {
// Get new and old values using pre-computed pointers
let (high, low, close, volume) = crate::extract_simd_inputs_at_index!(
i,
N,
high @ high_ptrs,
low @ low_ptrs,
close @ close_ptrs,
volume @ volume_ptrs
);
ads = calc_simd(ads, high, low, close, volume);
// Store results using pre-computed pointers
crate::write_simd_at_indices!(N, i,
ad_line_ptr => ads
);
}
// Update states efficiently
let final_ads = ads.to_array();
for (i, state) in states.iter_mut().enumerate().take(N) {
**state = final_ads[i];
}
}
}
/// Calculates the Accumulation/Distribution (AD) line for `N` assets simultaneously using SIMD
/// parallelism.
///
/// AD requires no configurable options. Uses the [`PrimeMover`] scheduler to batch assets into
/// SIMD-width groups.
///
/// # Arguments
/// * `inputs` - An array of `N` asset input sets; `inputs[i]` is `[&[f64]; INPUTS_WIDTH]`
/// containing `[high, low, close, volume]` for asset `i`.
/// * `options` - Unused; AD has no configurable options.
/// * `optional_outputs` - Unused; AD produces only the single AD line output.
///
/// # Returns
/// `Ok((outputs, states))` where `outputs[i][0]` is the AD line for asset `i`
/// and `states[i]` is the final [`IndicatorState`] for asset `i`.
/// Returns `Err(IndicatorError)` if any input slice is too short.
pub fn indicator_by_assets<const N: usize>(
inputs: &[&[&[f64]; INPUTS_WIDTH]; N], //stock[ fields [ field [f64] ] ]
_options: &[f64; OPTIONS_WIDTH],
_optional_outputs: Option<&[bool]>,
) -> Result<(Vec<Vec<Vec<f64>>>, Vec<IndicatorState>), IndicatorError> {
validate_inputs::<INPUTS_WIDTH>(inputs, min_data(_options))?;
let ads = [0.0; N];
let mut road_train = PrimeMover::<N, f64>::new();
let mut output_buffers: Vec<Vec<Vec<f64>>> = (0..N)
.map(|i| {
vec![{
let capacity = inputs[i][0].len();
crate::uninit_vec!(f64, capacity)
}]
})
.collect();
for i in 0..N {
let asset_inputs = vec![
inputs[i][0], // high
inputs[i][1], // low
inputs[i][2], // close
inputs[i][3], // volume
];
unsafe {
// Get a mutable reference to the output buffer for this asset
let output_buffer = &mut output_buffers[i][0];
let asset_outputs = vec![std::slice::from_raw_parts_mut(
output_buffer.as_mut_ptr(),
output_buffer.len(),
)];
road_train.add_asset(Asset::new(
asset_inputs,
asset_outputs,
i,
0,
0,
ads[i],
None,
));
}
}
let mut driver = AdDriver {};
let ads = road_train.drive(&mut driver);
let mut states = Vec::with_capacity(N);
for &ad in ads.iter() {
states.push(IndicatorState::new(ad));
}
Ok((output_buffers, states))
}