sakurs_core/application/execution_mode.rs
1//! Execution mode control for text processing
2//!
3//! This module provides a simple and effective way to control how text processing
4//! is executed (sequential vs parallel). We intentionally use this enum-based
5//! approach instead of the Strategy pattern for the following reasons:
6//!
7//! 1. **Simplicity**: The ExecutionMode enum clearly expresses the three modes
8//! of operation without unnecessary abstraction layers.
9//!
10//! 2. **Performance**: Direct mode selection avoids virtual dispatch overhead
11//! and keeps the hot path efficient.
12//!
13//! 3. **Maintainability**: All execution logic remains in DeltaStackProcessor,
14//! making it easier to understand and modify.
15//!
16//! 4. **Sufficient for current needs**: The three modes (Sequential, Parallel,
17//! and Adaptive) cover all current use cases effectively.
18//!
19//! If future requirements demand more complex execution strategies (e.g.,
20//! streaming, GPU acceleration), we can extend this enum or reconsider the
21//! architecture at that time.
22
23/// Represents the execution mode for text processing
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ExecutionMode {
26 /// Single-threaded sequential processing
27 Sequential,
28 /// Multi-threaded parallel processing with optional thread count
29 Parallel { threads: Option<usize> },
30 /// Adaptive mode that automatically selects the best strategy
31 Adaptive,
32}
33
34impl ExecutionMode {
35 /// Determines the actual number of threads to use based on the mode and text size
36 pub fn determine_thread_count(&self, text_len: usize) -> usize {
37 match self {
38 ExecutionMode::Sequential => 1,
39 ExecutionMode::Parallel { threads: Some(n) } => *n,
40 ExecutionMode::Parallel { threads: None } | ExecutionMode::Adaptive => {
41 Self::calculate_optimal_threads(text_len)
42 }
43 }
44 }
45
46 /// Calculates the optimal number of threads based on text size
47 /// This preserves the existing heuristics from UnifiedProcessor
48 fn calculate_optimal_threads(text_len: usize) -> usize {
49 const MIN_CHUNK_SIZE: usize = 256 * 1024; // 256KB per thread
50
51 if text_len < MIN_CHUNK_SIZE {
52 1
53 } else {
54 let available_parallelism = std::thread::available_parallelism()
55 .map(|n| n.get())
56 .unwrap_or(1);
57
58 let size_based_threads = (text_len / MIN_CHUNK_SIZE).max(1);
59 size_based_threads.min(available_parallelism)
60 }
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_sequential_mode() {
70 let mode = ExecutionMode::Sequential;
71 assert_eq!(mode.determine_thread_count(1_000_000), 1);
72 }
73
74 #[test]
75 fn test_parallel_mode_with_threads() {
76 let mode = ExecutionMode::Parallel { threads: Some(4) };
77 assert_eq!(mode.determine_thread_count(1_000_000), 4);
78 }
79
80 #[test]
81 fn test_adaptive_mode_small_text() {
82 let mode = ExecutionMode::Adaptive;
83 // Small text should use single thread
84 assert_eq!(mode.determine_thread_count(100_000), 1);
85 }
86
87 #[test]
88 fn test_adaptive_mode_large_text() {
89 let mode = ExecutionMode::Adaptive;
90 // Large text should use multiple threads
91 let thread_count = mode.determine_thread_count(10_000_000);
92 assert!(thread_count > 1);
93 }
94}