Skip to main content

lumen_core/ops/
fused.rs

1use crate::{NumDType, StorageIndices, WithDType};
2use crate::Tensor;
3use paste::paste;
4
5macro_rules! fused_2arg_inplace_op_impl {
6    ($name:ident, $f_inplace:expr, $f:expr) => {
7        paste! {
8            #[doc = crate::inplace_warning_doc!()]
9            pub fn [< $name _ >](&self, a: &Self, b: &Self) -> crate::Result<()> {
10                self.[< impl_ $name _ >](a, b)?;
11                self.impl_add_mul_(a, b)
12            }
13
14            #[doc = crate::inplace_warning_doc!()]
15            pub(crate) fn [< impl_ $name _ >](&self, a: &Self, b: &Self) -> crate::Result<()> {
16                self.fused_2arg_inplace_op(a, b, $f_inplace, $f, stringify!($name))
17            }
18        }
19    };
20}
21
22impl<T: NumDType> Tensor<T> {
23    fused_2arg_inplace_op_impl!(add_mul, T::add, T::mul);
24    fused_2arg_inplace_op_impl!(add_div, T::add, T::div);
25    fused_2arg_inplace_op_impl!(sub_mul, T::sub, T::mul);
26    fused_2arg_inplace_op_impl!(sub_div, T::sub, T::div);
27}
28
29impl<T: NumDType> Tensor<T> {
30    pub(crate) fn fused_2arg_inplace_op<F1, F2>(
31        &self,
32        src1: &Tensor<T>, 
33        src2: &Tensor<T>, 
34        f_inplace: F2, 
35        f: F1, 
36        op_name: &'static str
37    ) -> crate::Result<()> 
38    where 
39        F1: Fn(T, T) -> T,
40        F2: Fn(T, T) -> T,
41    {
42        let dst = self;
43        let _ = Tensor::<T>::same_shape_binary_op(dst, src1, op_name)?;
44        let _ = Tensor::<T>::same_shape_binary_op(dst, src2, op_name)?;
45
46        let mut dst_storage = dst.storage_write()?;
47        let src1_storage = src1.storage_read()?;
48        let src2_storage = src2.storage_read()?;
49        
50        let dst_layout = dst.layout();
51        let src1_layout = src1.layout();
52        let src2_layout = src2.layout();
53
54        let dst_data = dst_storage.data_mut();
55        let src1_data = src1_storage.data();
56        let src2_data = src2_storage.data();        
57
58        use StorageIndices::{Contiguous, Uncontiguous};
59
60        match (dst_layout.storage_indices(), src1_layout.storage_indices(), src2_layout.storage_indices()) {
61            // 1. (连续, 连续, 连续)
62            (Contiguous(d_idx), Contiguous(s1_idx), Contiguous(s2_idx)) => {
63                let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
64                let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
65                let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
66                
67                d_slice.iter_mut()
68                    .zip(s1_slice.iter())
69                    .zip(s2_slice.iter())
70                    .for_each(|((d, &s1), &s2)| {
71                        *d = f_inplace(*d, f(s1, s2));
72                    });
73            }
74
75            // 2. (连续, 连续, 不连续)
76            (Contiguous(d_idx), Contiguous(s1_idx), Uncontiguous(s2_idx)) => {
77                let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
78                let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
79
80                d_slice.iter_mut()
81                    .zip(s1_slice.iter())
82                    .zip(s2_idx)
83                    .for_each(|((d, &s1), s2_i)| {
84                        *d = f_inplace(*d, f(s1, src2_data[s2_i]));
85                    });
86            }
87
88            // 3. (连续, 不连续, 连续)
89            (Contiguous(d_idx), Uncontiguous(s1_idx), Contiguous(s2_idx)) => {
90                let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
91                let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
92
93                d_slice.iter_mut()
94                    .zip(s1_idx)
95                    .zip(s2_slice.iter())
96                    .for_each(|((d, s1_i), &s2)| {
97                        *d = f_inplace(*d, f(src1_data[s1_i], s2));
98                    });
99            }
100
101            // 4. (连续, 不连续, 不连续)
102            (Contiguous(d_idx), Uncontiguous(s1_idx), Uncontiguous(s2_idx)) => {
103                let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
104
105                d_slice.iter_mut()
106                    .zip(s1_idx)
107                    .zip(s2_idx)
108                    .for_each(|((d, s1_i), s2_i)| {
109                        *d = f_inplace(*d, f(src1_data[s1_i], src2_data[s2_i]));
110                    });
111            }
112
113            // 5. (不连续, 连续, 连续)
114            (Uncontiguous(d_idx), Contiguous(s1_idx), Contiguous(s2_idx)) => {
115                let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
116                let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
117
118                d_idx.zip(s1_slice.iter())
119                    .zip(s2_slice.iter())
120                    .for_each(|((d_i, &s1), &s2)| {
121                        dst_data[d_i] = f_inplace(dst_data[d_i], f(s1, s2));
122                    });
123            }
124
125            // 6. (不连续, 连续, 不连续)
126            (Uncontiguous(d_idx), Contiguous(s1_idx), Uncontiguous(s2_idx)) => {
127                let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
128
129                d_idx.zip(s1_slice.iter())
130                    .zip(s2_idx)
131                    .for_each(|((d_i, &s1), s2_i)| {
132                        dst_data[d_i] = f_inplace(dst_data[d_i], f(s1, src2_data[s2_i]));
133                    });
134            }
135
136            // 7. (不连续, 不连续, 连续)
137            (Uncontiguous(d_idx), Uncontiguous(s1_idx), Contiguous(s2_idx)) => {
138                let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
139
140                d_idx.zip(s1_idx)
141                    .zip(s2_slice.iter())
142                    .for_each(|((d_i, s1_i), &s2)| {
143                        dst_data[d_i] = f_inplace(dst_data[d_i], f(src1_data[s1_i], s2));
144                    });
145            }
146
147            // 8. (不连续, 不连续, 不连续) - 最慢的 fallback 路径
148            (Uncontiguous(d_idx), Uncontiguous(s1_idx), Uncontiguous(s2_idx)) => {
149                d_idx.zip(s1_idx)
150                    .zip(s2_idx)
151                    .for_each(|((d_i, s1_i), s2_i)| {
152                        let v = f(src1_data[s1_i], src2_data[s2_i]);
153                        dst_data[d_i] = f_inplace(dst_data[d_i], v);
154                    });
155            }
156        }
157        
158        Ok(())
159    }
160
161    pub(crate) fn fused_1arg_inplace_op<F1, F2>(
162        &self,
163        rhs: &Tensor<T>, 
164        f_inplace: F1, 
165        f: F2, 
166        op_name: &'static str
167    ) -> crate::Result<()> 
168    where 
169        F1: Fn(T, T) -> T,
170        F2: Fn(T) -> T,
171    {
172        let lhs = self;
173        let _ = Tensor::<T>::same_shape_binary_op(lhs, rhs, op_name)?;
174
175        let mut lhs_storage = lhs.storage_write()?;
176        let rhs_storage = rhs.storage_read()?;
177        let lhs_layout = lhs.layout();
178        let rhs_layout = rhs.layout();
179
180        assert_eq!(lhs_layout.dims(), rhs_layout.dims(), "lhs dims != rhs dim2");
181
182        let lhs = lhs_storage.data_mut();
183        let rhs = rhs_storage.data();
184        
185        match (lhs_layout.storage_indices(), rhs_layout.storage_indices()) {
186            (StorageIndices::Contiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
187                let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
188                let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
189                lhs.iter_mut().zip(rhs.iter())
190                    .for_each(|(l, &r)| *l = f_inplace(*l, f(r)));
191            }
192            (StorageIndices::Contiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
193                let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
194                lhs.iter_mut().zip(rhs_index)
195                    .for_each(|(l, rhs_index)| *l = f_inplace(*l, f(rhs[rhs_index])));
196            }
197            (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
198                let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
199                lhs_index.zip(rhs.iter())
200                    .for_each(|(lhs_index, &r)| lhs[lhs_index] = f_inplace(lhs[lhs_index], f(r)) );
201            }
202            (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
203                lhs_index.zip(rhs_index)
204                    .for_each(|(lhs_index, rhs_index)| lhs[lhs_index] = f_inplace(lhs[lhs_index], f(rhs[rhs_index])) );
205            }
206        }
207        
208        Ok(())
209    }
210}
211
212impl<T: WithDType> Tensor<T> {
213    pub fn check_implace_op(&self) -> crate::Result<()> {
214        // if self.requires_grad() {
215        //     return Err(crate::Error::InplaceOpInWhenRequiresGrad)
216        // }
217        Ok(())
218    }
219}