Skip to main content

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 {}