Skip to main content

tenflowers_core/device/
async_execution.rs

1use crate::{Device, Result, TensorError};
2use std::collections::VecDeque;
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::{Arc, Mutex};
6use std::task::{Context, Poll, Waker};
7
8/// Async operation handle
9pub struct AsyncOperation {
10    id: u64,
11    device: Device,
12    state: Arc<Mutex<OperationState>>,
13}
14
15/// State of an async operation
16#[derive(Debug)]
17enum OperationState {
18    Pending,
19    Running,
20    Completed,
21    Failed(String),
22}
23
24impl AsyncOperation {
25    pub fn new(id: u64, device: Device) -> Self {
26        Self {
27            id,
28            device,
29            state: Arc::new(Mutex::new(OperationState::Pending)),
30        }
31    }
32
33    /// Get operation ID
34    pub fn id(&self) -> u64 {
35        self.id
36    }
37
38    /// Get device
39    pub fn device(&self) -> Device {
40        self.device
41    }
42
43    /// Check if operation is complete
44    pub fn is_complete(&self) -> bool {
45        matches!(
46            *self
47                .state
48                .lock()
49                .unwrap_or_else(|poisoned| poisoned.into_inner()),
50            OperationState::Completed
51        )
52    }
53
54    /// Check if operation failed
55    pub fn is_failed(&self) -> bool {
56        matches!(
57            *self
58                .state
59                .lock()
60                .unwrap_or_else(|poisoned| poisoned.into_inner()),
61            OperationState::Failed(_)
62        )
63    }
64
65    /// Wait for operation to complete
66    pub async fn wait(&self) -> Result<()> {
67        AsyncWaitFuture::new(Arc::clone(&self.state)).await
68    }
69
70    /// Mark operation as completed
71    pub(crate) fn complete(&self) {
72        let mut state = self
73            .state
74            .lock()
75            .unwrap_or_else(|poisoned| poisoned.into_inner());
76        if matches!(*state, OperationState::Running) {
77            *state = OperationState::Completed;
78        }
79    }
80
81    /// Mark operation as failed
82    pub(crate) fn fail(&self, error: String) {
83        let mut state = self
84            .state
85            .lock()
86            .unwrap_or_else(|poisoned| poisoned.into_inner());
87        if matches!(*state, OperationState::Running) {
88            *state = OperationState::Failed(error);
89        }
90    }
91
92    /// Mark operation as running
93    pub(crate) fn start(&self) {
94        let mut state = self
95            .state
96            .lock()
97            .unwrap_or_else(|poisoned| poisoned.into_inner());
98        if matches!(*state, OperationState::Pending) {
99            *state = OperationState::Running;
100        }
101    }
102}
103
104/// Future for waiting on async operations
105struct AsyncWaitFuture {
106    state: Arc<Mutex<OperationState>>,
107    waker: Option<Waker>,
108}
109
110impl AsyncWaitFuture {
111    fn new(state: Arc<Mutex<OperationState>>) -> Self {
112        Self { state, waker: None }
113    }
114}
115
116impl Future for AsyncWaitFuture {
117    type Output = Result<()>;
118
119    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
120        let state = self
121            .state
122            .lock()
123            .unwrap_or_else(|poisoned| poisoned.into_inner());
124        match *state {
125            OperationState::Completed => Poll::Ready(Ok(())),
126            OperationState::Failed(ref err) => {
127                Poll::Ready(Err(TensorError::compute_error_simple(err.clone())))
128            }
129            OperationState::Pending | OperationState::Running => {
130                drop(state); // Release the lock before setting waker
131                self.waker = Some(cx.waker().clone());
132                Poll::Pending
133            }
134        }
135    }
136}
137
138/// Async execution engine with enhanced batching and priority support
139pub struct AsyncExecutor {
140    device: Device,
141    queue: Arc<Mutex<VecDeque<PendingOperation>>>,
142    high_priority_queue: Arc<Mutex<VecDeque<PendingOperation>>>,
143    next_id: Arc<Mutex<u64>>,
144    batch_size: usize,
145    is_processing: Arc<Mutex<bool>>,
146    stats: Arc<Mutex<ExecutorStats>>,
147}
148
149/// Execution statistics
150#[derive(Debug, Default, Clone)]
151pub struct ExecutorStats {
152    pub operations_completed: u64,
153    pub operations_failed: u64,
154    pub total_execution_time_us: u64,
155    pub average_queue_length: f64,
156    pub batch_efficiency: f64,
157}
158
159/// Operation priority level
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum Priority {
162    Low,
163    Normal,
164    High,
165    Critical,
166}
167
168/// Pending operation in the queue
169#[allow(dead_code)]
170struct PendingOperation {
171    operation: Box<dyn AsyncKernel>,
172    handle: AsyncOperation,
173    priority: Priority,
174    submitted_at: std::time::Instant,
175}
176
177/// Trait for async kernels with enhanced capabilities
178pub trait AsyncKernel: Send + Sync {
179    /// Execute the kernel asynchronously
180    fn execute(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>;
181
182    /// Get kernel name for debugging
183    fn name(&self) -> &str;
184
185    /// Get estimated execution time in microseconds
186    fn estimated_time_us(&self) -> u64 {
187        1000 // Default 1ms
188    }
189
190    /// Check if this kernel can be batched with another
191    fn can_batch_with(&self, _other: &dyn AsyncKernel) -> bool {
192        false // Default: no batching
193    }
194
195    /// Get kernel priority
196    fn priority(&self) -> Priority {
197        Priority::Normal
198    }
199
200    /// Get memory requirements in bytes
201    fn memory_requirements(&self) -> usize {
202        0 // Default: unknown
203    }
204
205    /// Check if kernel supports concurrent execution
206    fn supports_concurrency(&self) -> bool {
207        false // Default: sequential execution
208    }
209}
210
211impl AsyncExecutor {
212    /// Create a new async executor for a device
213    pub fn new(device: Device) -> Self {
214        Self::with_batch_size(device, 8) // Default batch size of 8
215    }
216
217    /// Create a new async executor with specified batch size
218    pub fn with_batch_size(device: Device, batch_size: usize) -> Self {
219        Self {
220            device,
221            queue: Arc::new(Mutex::new(VecDeque::new())),
222            high_priority_queue: Arc::new(Mutex::new(VecDeque::new())),
223            next_id: Arc::new(Mutex::new(0)),
224            batch_size,
225            is_processing: Arc::new(Mutex::new(false)),
226            stats: Arc::new(Mutex::new(ExecutorStats::default())),
227        }
228    }
229
230    /// Submit an async operation
231    pub fn submit(&self, kernel: Box<dyn AsyncKernel>) -> AsyncOperation {
232        self.submit_with_priority(kernel, Priority::Normal)
233    }
234
235    /// Submit an async operation with specified priority
236    pub fn submit_with_priority(
237        &self,
238        kernel: Box<dyn AsyncKernel>,
239        priority: Priority,
240    ) -> AsyncOperation {
241        let id = {
242            let mut next_id = self
243                .next_id
244                .lock()
245                .unwrap_or_else(|poisoned| poisoned.into_inner());
246            let id = *next_id;
247            *next_id += 1;
248            id
249        };
250
251        let handle = AsyncOperation::new(id, self.device);
252        let pending = PendingOperation {
253            operation: kernel,
254            handle: AsyncOperation::new(id, self.device),
255            priority,
256            submitted_at: std::time::Instant::now(),
257        };
258
259        // Select appropriate queue based on priority
260        match priority {
261            Priority::Critical | Priority::High => {
262                let mut queue = self
263                    .high_priority_queue
264                    .lock()
265                    .unwrap_or_else(|poisoned| poisoned.into_inner());
266                queue.push_back(pending);
267            }
268            Priority::Normal | Priority::Low => {
269                let mut queue = self
270                    .queue
271                    .lock()
272                    .unwrap_or_else(|poisoned| poisoned.into_inner());
273                queue.push_back(pending);
274            }
275        }
276
277        handle
278    }
279
280    /// Process pending operations with advanced batching and priority handling
281    pub async fn process_queue(&self) -> Result<()> {
282        // Check if already processing to avoid race conditions
283        {
284            let mut is_processing = self.is_processing.lock().map_err(|_| {
285                TensorError::invalid_operation_simple("is_processing lock poisoned".to_string())
286            })?;
287            if *is_processing {
288                return Ok(());
289            }
290            *is_processing = true;
291        }
292
293        let result = self.process_queue_internal().await;
294
295        // Release processing lock
296        {
297            let mut is_processing = self.is_processing.lock().map_err(|_| {
298                TensorError::invalid_operation_simple("is_processing lock poisoned".to_string())
299            })?;
300            *is_processing = false;
301        }
302
303        result
304    }
305
306    /// Internal queue processing logic
307    async fn process_queue_internal(&self) -> Result<()> {
308        // Process high priority queue first
309        if let Some(batch) = self.get_next_batch(true) {
310            self.execute_batch(batch).await?;
311        } else if let Some(batch) = self.get_next_batch(false) {
312            self.execute_batch(batch).await?;
313        }
314
315        Ok(())
316    }
317
318    /// Get next batch of operations to execute
319    fn get_next_batch(&self, high_priority: bool) -> Option<Vec<PendingOperation>> {
320        let queue = if high_priority {
321            &self.high_priority_queue
322        } else {
323            &self.queue
324        };
325
326        let mut queue_guard = queue
327            .lock()
328            .unwrap_or_else(|poisoned| poisoned.into_inner());
329        if queue_guard.is_empty() {
330            return None;
331        }
332
333        let mut batch = Vec::new();
334        let first_op = queue_guard
335            .pop_front()
336            .expect("queue should not be empty after is_empty check");
337        batch.push(first_op);
338
339        // Try to batch compatible operations
340        while batch.len() < self.batch_size && !queue_guard.is_empty() {
341            let next_op = queue_guard
342                .front()
343                .expect("queue should not be empty in while loop");
344
345            // Check if operations can be batched
346            if batch[0]
347                .operation
348                .can_batch_with(next_op.operation.as_ref())
349            {
350                batch.push(
351                    queue_guard
352                        .pop_front()
353                        .expect("queue should have front element we just checked"),
354                );
355            } else {
356                break;
357            }
358        }
359
360        Some(batch)
361    }
362
363    /// Execute a batch of operations
364    async fn execute_batch(&self, batch: Vec<PendingOperation>) -> Result<()> {
365        let start_time = std::time::Instant::now();
366        let batch_size = batch.len();
367
368        // Start all operations
369        for op in &batch {
370            op.handle.start();
371        }
372
373        // Execute operations (potentially in parallel for concurrent kernels)
374        let concurrent_ops: Vec<_> = batch
375            .iter()
376            .filter(|op| op.operation.supports_concurrency())
377            .collect();
378
379        let sequential_ops: Vec<_> = batch
380            .iter()
381            .filter(|op| !op.operation.supports_concurrency())
382            .collect();
383
384        // Execute concurrent operations in parallel
385        if !concurrent_ops.is_empty() {
386            let futures: Vec<_> = concurrent_ops
387                .iter()
388                .map(|op| op.operation.execute())
389                .collect();
390
391            // Wait for all concurrent operations
392            for (i, future) in futures.into_iter().enumerate() {
393                match future.await {
394                    Ok(()) => concurrent_ops[i].handle.complete(),
395                    Err(e) => concurrent_ops[i].handle.fail(e.to_string()),
396                }
397            }
398        }
399
400        // Execute sequential operations one by one
401        for op in sequential_ops {
402            match op.operation.execute().await {
403                Ok(()) => op.handle.complete(),
404                Err(e) => op.handle.fail(e.to_string()),
405            }
406        }
407
408        // Update statistics
409        let execution_time = start_time.elapsed();
410        self.update_stats(batch_size, execution_time, true);
411
412        Ok(())
413    }
414
415    /// Update execution statistics
416    fn update_stats(&self, batch_size: usize, execution_time: std::time::Duration, success: bool) {
417        let mut stats = self
418            .stats
419            .lock()
420            .unwrap_or_else(|poisoned| poisoned.into_inner());
421
422        if success {
423            stats.operations_completed += batch_size as u64;
424        } else {
425            stats.operations_failed += batch_size as u64;
426        }
427
428        stats.total_execution_time_us += execution_time.as_micros() as u64;
429
430        // Update batch efficiency (higher is better)
431        stats.batch_efficiency = batch_size as f64;
432
433        // Update average queue length
434        let current_queue_len = self.queue_length() as f64;
435        stats.average_queue_length = (stats.average_queue_length * 0.9) + (current_queue_len * 0.1);
436    }
437
438    /// Get total queue length (both normal and high priority)
439    pub fn queue_length(&self) -> usize {
440        let normal_len = self
441            .queue
442            .lock()
443            .unwrap_or_else(|poisoned| poisoned.into_inner())
444            .len();
445        let high_len = self
446            .high_priority_queue
447            .lock()
448            .unwrap_or_else(|poisoned| poisoned.into_inner())
449            .len();
450        normal_len + high_len
451    }
452
453    /// Get queue length by priority
454    pub fn queue_length_by_priority(&self, high_priority: bool) -> usize {
455        if high_priority {
456            self.high_priority_queue
457                .lock()
458                .unwrap_or_else(|poisoned| poisoned.into_inner())
459                .len()
460        } else {
461            self.queue
462                .lock()
463                .unwrap_or_else(|poisoned| poisoned.into_inner())
464                .len()
465        }
466    }
467
468    /// Clear all pending operations
469    pub fn clear_queue(&self) {
470        // Clear normal priority queue
471        {
472            let mut queue = self
473                .queue
474                .lock()
475                .unwrap_or_else(|poisoned| poisoned.into_inner());
476            for op in queue.drain(..) {
477                op.handle.fail("Operation cancelled".to_string());
478            }
479        }
480
481        // Clear high priority queue
482        {
483            let mut queue = self
484                .high_priority_queue
485                .lock()
486                .unwrap_or_else(|poisoned| poisoned.into_inner());
487            for op in queue.drain(..) {
488                op.handle.fail("Operation cancelled".to_string());
489            }
490        }
491    }
492
493    /// Get execution statistics
494    pub fn get_stats(&self) -> ExecutorStats {
495        self.stats
496            .lock()
497            .unwrap_or_else(|poisoned| poisoned.into_inner())
498            .clone()
499    }
500
501    /// Reset execution statistics
502    pub fn reset_stats(&self) {
503        let mut stats = self
504            .stats
505            .lock()
506            .unwrap_or_else(|poisoned| poisoned.into_inner());
507        *stats = ExecutorStats::default();
508    }
509
510    /// Get device this executor manages
511    pub fn device(&self) -> Device {
512        self.device
513    }
514
515    /// Check if executor is currently processing
516    pub fn is_processing(&self) -> bool {
517        *self
518            .is_processing
519            .lock()
520            .unwrap_or_else(|poisoned| poisoned.into_inner())
521    }
522}
523
524/// Global async executor manager
525pub struct AsyncExecutorManager {
526    executors: Arc<Mutex<std::collections::HashMap<Device, Arc<AsyncExecutor>>>>,
527}
528
529impl AsyncExecutorManager {
530    /// Create a new executor manager
531    pub fn new() -> Self {
532        Self {
533            executors: Arc::new(Mutex::new(std::collections::HashMap::new())),
534        }
535    }
536
537    /// Get or create executor for a device
538    pub fn get_executor(&self, device: Device) -> Arc<AsyncExecutor> {
539        let mut executors = self
540            .executors
541            .lock()
542            .unwrap_or_else(|poisoned| poisoned.into_inner());
543
544        if let Some(executor) = executors.get(&device) {
545            Arc::clone(executor)
546        } else {
547            let executor = Arc::new(AsyncExecutor::new(device));
548            executors.insert(device, Arc::clone(&executor));
549            executor
550        }
551    }
552
553    /// Process all device queues
554    pub async fn process_all(&self) -> Result<()> {
555        let executors = {
556            let executors = self.executors.lock().map_err(|_| {
557                TensorError::invalid_operation_simple("executors lock poisoned".to_string())
558            })?;
559            executors.values().cloned().collect::<Vec<_>>()
560        };
561
562        for executor in executors {
563            executor.process_queue().await?;
564        }
565
566        Ok(())
567    }
568}
569
570impl Default for AsyncExecutorManager {
571    fn default() -> Self {
572        Self::new()
573    }
574}
575
576lazy_static::lazy_static! {
577    /// Global executor manager instance
578    pub static ref ASYNC_EXECUTOR_MANAGER: AsyncExecutorManager = AsyncExecutorManager::new();
579}
580
581/// Submit an async operation to the appropriate device executor
582pub fn submit_async_operation(device: Device, kernel: Box<dyn AsyncKernel>) -> AsyncOperation {
583    let executor = ASYNC_EXECUTOR_MANAGER.get_executor(device);
584    executor.submit(kernel)
585}
586
587/// Example async kernel implementations
588pub mod kernels {
589    use super::*;
590    use std::time::Duration;
591
592    /// Dummy async kernel for testing
593    pub struct DummyKernel {
594        name: String,
595        delay_ms: u64,
596    }
597
598    impl DummyKernel {
599        pub fn new(name: String, delay_ms: u64) -> Self {
600            Self { name, delay_ms }
601        }
602    }
603
604    impl AsyncKernel for DummyKernel {
605        fn execute(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
606            let delay = Duration::from_millis(self.delay_ms);
607            Box::pin(async move {
608                // Simple delay simulation without tokio
609                std::thread::sleep(delay);
610                Ok(())
611            })
612        }
613
614        fn name(&self) -> &str {
615            &self.name
616        }
617
618        fn estimated_time_us(&self) -> u64 {
619            self.delay_ms * 1000
620        }
621    }
622
623    /// Matrix multiplication async kernel
624    pub struct MatMulKernel {
625        pub m: usize,
626        pub k: usize,
627        pub n: usize,
628        pub batch_size: usize,
629    }
630
631    impl AsyncKernel for MatMulKernel {
632        fn execute(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
633            // In real implementation, this would launch GPU compute shader
634            let ops = self.batch_size * self.m * self.k * self.n * 2;
635            let estimated_time_us = (ops / 1_000_000).max(100); // At least 100us
636
637            Box::pin(async move {
638                // Simulate GPU execution time
639                std::thread::sleep(Duration::from_micros(estimated_time_us as u64));
640                Ok(())
641            })
642        }
643
644        fn name(&self) -> &str {
645            "matmul"
646        }
647
648        fn estimated_time_us(&self) -> u64 {
649            let ops = self.batch_size * self.m * self.k * self.n * 2;
650            (ops / 1_000_000).max(100) as u64
651        }
652    }
653
654    /// Convolution async kernel
655    pub struct ConvKernel {
656        pub batch_size: usize,
657        pub in_channels: usize,
658        pub out_channels: usize,
659        pub input_size: (usize, usize),
660        pub kernel_size: (usize, usize),
661    }
662
663    impl AsyncKernel for ConvKernel {
664        fn execute(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
665            // Estimate convolution complexity
666            let ops = self.batch_size
667                * self.out_channels
668                * self.input_size.0
669                * self.input_size.1
670                * self.in_channels
671                * self.kernel_size.0
672                * self.kernel_size.1
673                * 2;
674            let estimated_time_us = (ops / 10_000_000).max(50); // Faster than matmul
675
676            Box::pin(async move {
677                std::thread::sleep(Duration::from_micros(estimated_time_us as u64));
678                Ok(())
679            })
680        }
681
682        fn name(&self) -> &str {
683            "conv2d"
684        }
685
686        fn estimated_time_us(&self) -> u64 {
687            let ops = self.batch_size
688                * self.out_channels
689                * self.input_size.0
690                * self.input_size.1
691                * self.in_channels
692                * self.kernel_size.0
693                * self.kernel_size.1
694                * 2;
695            (ops / 10_000_000).max(50) as u64
696        }
697
698        fn supports_concurrency(&self) -> bool {
699            true // Conv operations can often run concurrently
700        }
701    }
702
703    /// GPU Tensor manipulation async kernel
704    pub struct TensorManipulationKernel {
705        pub operation: String,
706        pub input_size: usize,
707        pub output_size: usize,
708        pub complexity_factor: f64, // Operation-specific complexity multiplier
709    }
710
711    impl TensorManipulationKernel {
712        pub fn new_gather(input_size: usize, indices_size: usize) -> Self {
713            Self {
714                operation: "gather".to_string(),
715                input_size,
716                output_size: indices_size,
717                complexity_factor: 1.2, // Gather has some indexing overhead
718            }
719        }
720
721        pub fn new_scatter(tensor_size: usize, updates_size: usize) -> Self {
722            Self {
723                operation: "scatter".to_string(),
724                input_size: tensor_size + updates_size,
725                output_size: tensor_size,
726                complexity_factor: 1.5, // Scatter has more complex indexing
727            }
728        }
729
730        pub fn new_roll(size: usize) -> Self {
731            Self {
732                operation: "roll".to_string(),
733                input_size: size,
734                output_size: size,
735                complexity_factor: 1.0, // Roll is relatively simple
736            }
737        }
738
739        pub fn new_where(_condition_size: usize, total_size: usize) -> Self {
740            Self {
741                operation: "where".to_string(),
742                input_size: total_size,
743                output_size: total_size,
744                complexity_factor: 0.8, // Where is simple conditional logic
745            }
746        }
747    }
748
749    impl AsyncKernel for TensorManipulationKernel {
750        fn execute(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
751            let base_time =
752                (self.input_size as f64 * self.complexity_factor / 1_000_000.0).max(10.0);
753            let estimated_time_us = base_time as u64;
754
755            Box::pin(async move {
756                // Simulate GPU kernel execution
757                std::thread::sleep(Duration::from_micros(estimated_time_us));
758                Ok(())
759            })
760        }
761
762        fn name(&self) -> &str {
763            &self.operation
764        }
765
766        fn estimated_time_us(&self) -> u64 {
767            (self.input_size as f64 * self.complexity_factor / 1_000_000.0).max(10.0) as u64
768        }
769
770        fn can_batch_with(&self, other: &dyn AsyncKernel) -> bool {
771            // Manipulation operations of the same type can potentially be batched
772            other.name() == self.name()
773        }
774
775        fn priority(&self) -> Priority {
776            match self.operation.as_str() {
777                "gather" | "scatter" => Priority::High, // Data movement is high priority
778                "where" => Priority::Normal,
779                _ => Priority::Low,
780            }
781        }
782
783        fn supports_concurrency(&self) -> bool {
784            match self.operation.as_str() {
785                "roll" | "where" => true,      // These can run concurrently
786                "gather" | "scatter" => false, // These may have data dependencies
787                _ => false,
788            }
789        }
790
791        fn memory_requirements(&self) -> usize {
792            self.input_size + self.output_size
793        }
794    }
795
796    /// Reduction operations async kernel
797    pub struct ReductionKernel {
798        pub operation: String,
799        pub input_size: usize,
800        pub output_size: usize,
801        pub num_axes: usize,
802    }
803
804    impl ReductionKernel {
805        pub fn new_sum(input_size: usize, output_size: usize, num_axes: usize) -> Self {
806            Self {
807                operation: "sum".to_string(),
808                input_size,
809                output_size,
810                num_axes,
811            }
812        }
813
814        pub fn new_mean(input_size: usize, output_size: usize, num_axes: usize) -> Self {
815            Self {
816                operation: "mean".to_string(),
817                input_size,
818                output_size,
819                num_axes,
820            }
821        }
822
823        pub fn new_max(input_size: usize, output_size: usize, num_axes: usize) -> Self {
824            Self {
825                operation: "max".to_string(),
826                input_size,
827                output_size,
828                num_axes,
829            }
830        }
831    }
832
833    impl AsyncKernel for ReductionKernel {
834        fn execute(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
835            // Reduction complexity depends on input size and number of reduction steps
836            let complexity = self.input_size as f64 * (self.num_axes as f64).log2();
837            let estimated_time_us = (complexity / 5_000_000.0).max(5.0) as u64;
838
839            Box::pin(async move {
840                std::thread::sleep(Duration::from_micros(estimated_time_us));
841                Ok(())
842            })
843        }
844
845        fn name(&self) -> &str {
846            &self.operation
847        }
848
849        fn estimated_time_us(&self) -> u64 {
850            let complexity = self.input_size as f64 * (self.num_axes as f64).log2();
851            (complexity / 5_000_000.0).max(5.0) as u64
852        }
853
854        fn can_batch_with(&self, other: &dyn AsyncKernel) -> bool {
855            // Reduction operations can sometimes be fused
856            other.name().starts_with("sum")
857                || other.name().starts_with("mean")
858                || other.name().starts_with("max")
859        }
860
861        fn priority(&self) -> Priority {
862            Priority::Normal
863        }
864
865        fn supports_concurrency(&self) -> bool {
866            true // Reductions can often run concurrently
867        }
868
869        fn memory_requirements(&self) -> usize {
870            self.input_size + self.output_size
871        }
872    }
873}
874
875#[cfg(test)]
876mod tests {
877    use super::kernels::*;
878    use super::*;
879
880    #[test]
881    fn test_async_operation() {
882        // Simple test without async runtime
883        // futures::executor::block_on(async {
884        let executor = AsyncExecutor::new(Device::Cpu);
885        let kernel = Box::new(DummyKernel::new("test".to_string(), 10));
886
887        let handle = executor.submit(kernel);
888        assert!(!handle.is_complete());
889
890        // For now, just test basic functionality without async
891        assert_eq!(handle.id(), 0);
892        assert_eq!(handle.device(), Device::Cpu);
893        // });
894    }
895
896    #[test]
897    fn test_multiple_operations() {
898        let executor = AsyncExecutor::new(Device::Cpu);
899
900        // Submit multiple operations
901        let handles = (0..5)
902            .map(|i| {
903                let kernel = Box::new(DummyKernel::new(format!("test_{i}"), 5));
904                executor.submit(kernel)
905            })
906            .collect::<Vec<_>>();
907
908        // Test that operations have sequential IDs
909        for (i, handle) in handles.iter().enumerate() {
910            assert_eq!(handle.id(), i as u64);
911        }
912
913        assert_eq!(executor.queue_length(), 5);
914    }
915
916    #[test]
917    fn test_matmul_kernel_estimation() {
918        let kernel = MatMulKernel {
919            m: 1024,
920            k: 1024,
921            n: 1024,
922            batch_size: 1,
923        };
924
925        let estimated_time = kernel.estimated_time_us();
926        assert!(estimated_time > 0);
927
928        // Larger operations should take longer
929        let large_kernel = MatMulKernel {
930            m: 2048,
931            k: 2048,
932            n: 2048,
933            batch_size: 1,
934        };
935
936        assert!(large_kernel.estimated_time_us() > estimated_time);
937    }
938
939    #[test]
940    fn test_tensor_manipulation_kernels() {
941        // Test gather kernel
942        let gather_kernel = TensorManipulationKernel::new_gather(10000, 1000);
943        assert_eq!(gather_kernel.name(), "gather");
944        assert_eq!(gather_kernel.priority(), Priority::High);
945        assert!(!gather_kernel.supports_concurrency());
946
947        // Test scatter kernel
948        let scatter_kernel = TensorManipulationKernel::new_scatter(10000, 2000);
949        assert_eq!(scatter_kernel.name(), "scatter");
950        assert_eq!(scatter_kernel.priority(), Priority::High);
951        assert!(!scatter_kernel.supports_concurrency());
952
953        // Test roll kernel
954        let roll_kernel = TensorManipulationKernel::new_roll(5000);
955        assert_eq!(roll_kernel.name(), "roll");
956        assert_eq!(roll_kernel.priority(), Priority::Low);
957        assert!(roll_kernel.supports_concurrency());
958
959        // Test where kernel
960        let where_kernel = TensorManipulationKernel::new_where(1000, 5000);
961        assert_eq!(where_kernel.name(), "where");
962        assert_eq!(where_kernel.priority(), Priority::Normal);
963        assert!(where_kernel.supports_concurrency());
964    }
965
966    #[test]
967    fn test_reduction_kernels() {
968        // Test sum reduction
969        let sum_kernel = ReductionKernel::new_sum(10000, 100, 2);
970        assert_eq!(sum_kernel.name(), "sum");
971        assert_eq!(sum_kernel.priority(), Priority::Normal);
972        assert!(sum_kernel.supports_concurrency());
973
974        // Test mean reduction
975        let mean_kernel = ReductionKernel::new_mean(20000, 200, 3);
976        assert_eq!(mean_kernel.name(), "mean");
977        assert!(mean_kernel.estimated_time_us() > 0);
978
979        // Test max reduction
980        let max_kernel = ReductionKernel::new_max(15000, 150, 2);
981        assert_eq!(max_kernel.name(), "max");
982
983        // Test batching between reduction operations
984        assert!(sum_kernel.can_batch_with(&mean_kernel));
985        assert!(mean_kernel.can_batch_with(&max_kernel));
986    }
987
988    #[test]
989    fn test_kernel_batching() {
990        let gather1 = TensorManipulationKernel::new_gather(1000, 100);
991        let gather2 = TensorManipulationKernel::new_gather(2000, 200);
992        let roll = TensorManipulationKernel::new_roll(1000);
993
994        // Same operation types should be batchable
995        assert!(gather1.can_batch_with(&gather2));
996
997        // Different operation types should not be batchable
998        assert!(!gather1.can_batch_with(&roll));
999    }
1000
1001    #[test]
1002    fn test_conv_kernel_concurrency() {
1003        let conv_kernel = ConvKernel {
1004            batch_size: 8,
1005            in_channels: 64,
1006            out_channels: 128,
1007            input_size: (224, 224),
1008            kernel_size: (3, 3),
1009        };
1010
1011        assert!(conv_kernel.supports_concurrency());
1012        assert_eq!(conv_kernel.name(), "conv2d");
1013        assert!(conv_kernel.estimated_time_us() > 0);
1014    }
1015}