kunquant_rs/executor.rs
1use crate::error::{KunQuantError, Result};
2use crate::ffi;
3
4/// A KunQuant executor responsible for running factor computations.
5///
6/// The `Executor` manages the computational resources and threading model used
7/// for factor calculations. It provides both single-threaded and multi-threaded
8/// execution modes to optimize performance based on workload characteristics.
9///
10/// # Thread Safety
11///
12/// - The executor itself is thread-safe and can be shared across threads
13/// - Multiple computations can be executed concurrently using the same executor
14/// - Each computation maintains its own state and memory buffers
15///
16/// # Memory Management
17///
18/// The executor automatically manages its resources using RAII. The underlying
19/// C handle is properly cleaned up when the executor is dropped.
20///
21/// # Performance Considerations
22///
23/// - Single-threaded executors have lower overhead for small computations
24/// - Multi-threaded executors scale better for large factor libraries
25/// - Thread count should typically match CPU core count for optimal performance
26pub struct Executor {
27 handle: ffi::KunExecutorHandle,
28}
29
30impl Executor {
31 /// Creates a single-threaded executor optimized for low-latency computations.
32 ///
33 /// Single-threaded executors are ideal for:
34 /// - Real-time streaming applications where latency is critical
35 /// - Simple factors with low computational complexity
36 /// - Scenarios where thread synchronization overhead outweighs benefits
37 /// - Development and testing environments
38 ///
39 /// # Returns
40 ///
41 /// Returns `Ok(Executor)` on success, or `Err(KunQuantError::ExecutorCreationFailed)`
42 /// if the underlying C library fails to create the executor.
43 ///
44 /// # Examples
45 ///
46 /// ```rust,no_run
47 /// use kunquant_rs::Executor;
48 ///
49 /// # fn main() -> kunquant_rs::Result<()> {
50 /// // Create a single-threaded executor for low-latency processing
51 /// let executor = Executor::single_thread()?;
52 ///
53 /// // Use with streaming context for real-time factor calculation
54 /// // let stream = StreamContext::new(&executor, &module, 16)?;
55 /// # Ok(())
56 /// # }
57 /// ```
58 ///
59 /// # Performance Notes
60 ///
61 /// - Minimal thread synchronization overhead
62 /// - Predictable execution timing
63 /// - Lower memory footprint compared to multi-threaded executors
64 /// - Best suited for factors processing fewer than 1000 stocks
65 pub fn single_thread() -> Result<Self> {
66 let handle = unsafe { ffi::kunCreateSingleThreadExecutor() };
67 if handle.is_null() {
68 return Err(KunQuantError::ExecutorCreationFailed);
69 }
70 Ok(Executor { handle })
71 }
72
73 /// Creates a multi-threaded executor for high-throughput batch computations.
74 ///
75 /// Multi-threaded executors are ideal for:
76 /// - Large-scale batch processing of historical data
77 /// - Complex factors with high computational requirements
78 /// - Processing thousands of stocks simultaneously
79 /// - Scenarios where throughput is more important than latency
80 ///
81 /// # Arguments
82 ///
83 /// * `num_threads` - Number of worker threads to create. Should typically match
84 /// the number of CPU cores for optimal performance. Values
85 /// less than 1 will result in creation failure.
86 ///
87 /// # Returns
88 ///
89 /// Returns `Ok(Executor)` on success, or `Err(KunQuantError::ExecutorCreationFailed)`
90 /// if:
91 /// - `num_threads` is less than 1
92 /// - The underlying C library fails to create the executor
93 /// - System resources are insufficient for the requested thread count
94 ///
95 /// # Examples
96 ///
97 /// ```rust,no_run
98 /// use kunquant_rs::Executor;
99 ///
100 /// # fn main() -> kunquant_rs::Result<()> {
101 /// // Create executor with 4 worker threads
102 /// let executor = Executor::multi_thread(4)?;
103 ///
104 /// // Optimal for CPU-bound batch processing
105 /// let cpu_cores = std::thread::available_parallelism().unwrap().get() as i32;
106 /// let optimal_executor = Executor::multi_thread(cpu_cores)?;
107 /// # Ok(())
108 /// # }
109 /// ```
110 ///
111 /// # Performance Notes
112 ///
113 /// - Scales well with CPU core count for compute-intensive factors
114 /// - Higher memory usage due to per-thread buffers
115 /// - Thread synchronization adds latency overhead
116 /// - Best suited for batch processing of large datasets
117 /// - Diminishing returns beyond CPU core count due to memory bandwidth limits
118 pub fn multi_thread(num_threads: i32) -> Result<Self> {
119 let handle = unsafe { ffi::kunCreateMultiThreadExecutor(num_threads) };
120 if handle.is_null() {
121 return Err(KunQuantError::ExecutorCreationFailed);
122 }
123 Ok(Executor { handle })
124 }
125
126 /// Get the raw handle (for internal use)
127 pub(crate) fn handle(&self) -> ffi::KunExecutorHandle {
128 self.handle
129 }
130}
131
132impl Drop for Executor {
133 fn drop(&mut self) {
134 if !self.handle.is_null() {
135 unsafe {
136 ffi::kunDestoryExecutor(self.handle);
137 }
138 }
139 }
140}
141
142// Executor is thread-safe according to KunQuant documentation
143unsafe impl Send for Executor {}
144unsafe impl Sync for Executor {}