1#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
2use super::config::SIMDInstructionSet;
3#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
4use super::generic::{impl_SIMDArgMinMax, impl_SIMDInit_Int};
5#[cfg(any(
6 target_arch = "x86",
7 target_arch = "x86_64",
8 all(target_arch = "arm", feature = "nightly_simd"),
9 target_arch = "aarch64",
10))]
11use super::generic::{SIMDArgMinMax, SIMDInit, SIMDOps};
12#[cfg(any(
13 target_arch = "x86",
14 target_arch = "x86_64",
15 all(target_arch = "arm", feature = "nightly_simd"),
16 target_arch = "aarch64",
17))]
18use crate::SCALAR;
19#[cfg(target_arch = "aarch64")]
20use std::arch::aarch64::*;
21#[cfg(target_arch = "x86")]
22use std::arch::x86::*;
23#[cfg(target_arch = "x86_64")]
24use std::arch::x86_64::*;
25
26#[cfg(any(
28 target_arch = "x86",
29 target_arch = "x86_64",
30 all(target_arch = "arm", feature = "nightly_simd"),
31 target_arch = "aarch64",
32))]
33use super::super::dtype_strategy::Int;
34
35#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
36const MAX_INDEX: usize = i64::MAX as usize;
37
38#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
41mod avx2 {
42 use super::super::config::AVX2;
43 use super::*;
44
45 const LANE_SIZE: usize = AVX2::<Int>::LANE_SIZE_64;
46
47 impl SIMDOps<i64, __m256i, __m256i, LANE_SIZE> for AVX2<Int> {
48 const INITIAL_INDEX: __m256i = unsafe { std::mem::transmute([0i64, 1i64, 2i64, 3i64]) };
49 const INDEX_INCREMENT: __m256i =
50 unsafe { std::mem::transmute([LANE_SIZE as i64; LANE_SIZE]) };
51 const MAX_INDEX: usize = MAX_INDEX;
52
53 #[inline(always)]
54 unsafe fn _reg_to_arr(reg: __m256i) -> [i64; LANE_SIZE] {
55 std::mem::transmute::<__m256i, [i64; LANE_SIZE]>(reg)
56 }
57
58 #[inline(always)]
59 unsafe fn _mm_loadu(data: *const i64) -> __m256i {
60 _mm256_loadu_si256(data as *const __m256i)
61 }
62
63 #[inline(always)]
64 unsafe fn _mm_add(a: __m256i, b: __m256i) -> __m256i {
65 _mm256_add_epi64(a, b)
66 }
67
68 #[inline(always)]
69 unsafe fn _mm_cmpgt(a: __m256i, b: __m256i) -> __m256i {
70 _mm256_cmpgt_epi64(a, b)
71 }
72
73 #[inline(always)]
74 unsafe fn _mm_cmplt(a: __m256i, b: __m256i) -> __m256i {
75 _mm256_cmpgt_epi64(b, a)
76 }
77
78 #[inline(always)]
79 unsafe fn _mm_blendv(a: __m256i, b: __m256i, mask: __m256i) -> __m256i {
80 _mm256_blendv_epi8(a, b, mask)
81 }
82 }
83
84 impl_SIMDInit_Int!(i64, __m256i, __m256i, LANE_SIZE, AVX2<Int>);
85
86 impl_SIMDArgMinMax!(
87 i64,
88 __m256i,
89 __m256i,
90 LANE_SIZE,
91 SCALAR<Int>,
92 AVX2<Int>,
93 "avx2"
94 );
95}
96
97#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
100mod sse {
101 use super::super::config::SSE;
102 use super::*;
103
104 const LANE_SIZE: usize = SSE::<Int>::LANE_SIZE_64;
105
106 impl SIMDOps<i64, __m128i, __m128i, LANE_SIZE> for SSE<Int> {
107 const INITIAL_INDEX: __m128i = unsafe { std::mem::transmute([0i64, 1i64]) };
108 const INDEX_INCREMENT: __m128i =
109 unsafe { std::mem::transmute([LANE_SIZE as i64; LANE_SIZE]) };
110 const MAX_INDEX: usize = MAX_INDEX;
111
112 #[inline(always)]
113 unsafe fn _reg_to_arr(reg: __m128i) -> [i64; LANE_SIZE] {
114 std::mem::transmute::<__m128i, [i64; LANE_SIZE]>(reg)
115 }
116
117 #[inline(always)]
118 unsafe fn _mm_loadu(data: *const i64) -> __m128i {
119 _mm_loadu_si128(data as *const __m128i)
120 }
121
122 #[inline(always)]
123 unsafe fn _mm_add(a: __m128i, b: __m128i) -> __m128i {
124 _mm_add_epi64(a, b)
125 }
126
127 #[inline(always)]
128 unsafe fn _mm_cmpgt(a: __m128i, b: __m128i) -> __m128i {
129 _mm_cmpgt_epi64(a, b)
130 }
131
132 #[inline(always)]
133 unsafe fn _mm_cmplt(a: __m128i, b: __m128i) -> __m128i {
134 _mm_cmpgt_epi64(b, a)
135 }
136
137 #[inline(always)]
138 unsafe fn _mm_blendv(a: __m128i, b: __m128i, mask: __m128i) -> __m128i {
139 _mm_blendv_epi8(a, b, mask)
140 }
141 }
142
143 impl_SIMDInit_Int!(i64, __m128i, __m128i, LANE_SIZE, SSE<Int>);
144
145 impl_SIMDArgMinMax!(
146 i64,
147 __m128i,
148 __m128i,
149 LANE_SIZE,
150 SCALAR<Int>,
151 SSE<Int>,
152 "sse4.2"
153 );
154}
155
156#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
159#[cfg(feature = "nightly_simd")]
160mod avx512 {
161 use super::super::config::AVX512;
162 use super::*;
163
164 const LANE_SIZE: usize = AVX512::<Int>::LANE_SIZE_64;
165
166 impl SIMDOps<i64, __m512i, u8, LANE_SIZE> for AVX512<Int> {
167 const INITIAL_INDEX: __m512i =
168 unsafe { std::mem::transmute([0i64, 1i64, 2i64, 3i64, 4i64, 5i64, 6i64, 7i64]) };
169 const INDEX_INCREMENT: __m512i =
170 unsafe { std::mem::transmute([LANE_SIZE as i64; LANE_SIZE]) };
171 const MAX_INDEX: usize = MAX_INDEX;
172
173 #[inline(always)]
174 unsafe fn _reg_to_arr(reg: __m512i) -> [i64; LANE_SIZE] {
175 std::mem::transmute::<__m512i, [i64; LANE_SIZE]>(reg)
176 }
177
178 #[inline(always)]
179 unsafe fn _mm_loadu(data: *const i64) -> __m512i {
180 _mm512_loadu_epi64(data as *const i64)
181 }
182
183 #[inline(always)]
184 unsafe fn _mm_add(a: __m512i, b: __m512i) -> __m512i {
185 _mm512_add_epi64(a, b)
186 }
187
188 #[inline(always)]
189 unsafe fn _mm_cmpgt(a: __m512i, b: __m512i) -> u8 {
190 _mm512_cmpgt_epi64_mask(a, b)
191 }
192
193 #[inline(always)]
194 unsafe fn _mm_cmplt(a: __m512i, b: __m512i) -> u8 {
195 _mm512_cmpgt_epi64_mask(b, a)
196 }
197
198 #[inline(always)]
199 unsafe fn _mm_blendv(a: __m512i, b: __m512i, mask: u8) -> __m512i {
200 _mm512_mask_blend_epi64(mask, a, b)
201 }
202 }
203
204 impl_SIMDInit_Int!(i64, __m512i, u8, LANE_SIZE, AVX512<Int>);
205
206 impl_SIMDArgMinMax!(
207 i64,
208 __m512i,
209 u8,
210 LANE_SIZE,
211 SCALAR<Int>,
212 AVX512<Int>,
213 "avx512f"
214 );
215}
216
217#[cfg(target_arch = "arm")]
224#[cfg(feature = "nightly_simd")]
225mod neon {
226 use super::super::config::NEON;
227 use super::super::generic::{unimpl_SIMDArgMinMax, unimpl_SIMDInit, unimpl_SIMDOps};
228 use super::*;
229
230 unimpl_SIMDOps!(i64, usize, NEON<Int>);
235 unimpl_SIMDInit!(i64, usize, NEON<Int>);
236 unimpl_SIMDArgMinMax!(i64, usize, SCALAR<Int>, NEON<Int>);
237}
238
239#[cfg(target_arch = "aarch64")] mod neon {
241 use super::super::config::NEON;
242 use super::*;
243
244 const LANE_SIZE: usize = NEON::<Int>::LANE_SIZE_64;
245
246 impl SIMDOps<i64, int64x2_t, uint64x2_t, LANE_SIZE> for NEON<Int> {
247 const INITIAL_INDEX: int64x2_t = unsafe { std::mem::transmute([0i64, 1i64]) };
248 const INDEX_INCREMENT: int64x2_t =
249 unsafe { std::mem::transmute([LANE_SIZE as i64; LANE_SIZE]) };
250 const MAX_INDEX: usize = MAX_INDEX;
251
252 #[inline(always)]
253 unsafe fn _reg_to_arr(reg: int64x2_t) -> [i64; LANE_SIZE] {
254 std::mem::transmute::<int64x2_t, [i64; LANE_SIZE]>(reg)
255 }
256
257 #[inline(always)]
258 unsafe fn _mm_loadu(data: *const i64) -> int64x2_t {
259 vld1q_s64(data)
260 }
261
262 #[inline(always)]
263 unsafe fn _mm_add(a: int64x2_t, b: int64x2_t) -> int64x2_t {
264 vaddq_s64(a, b)
265 }
266
267 #[inline(always)]
268 unsafe fn _mm_cmpgt(a: int64x2_t, b: int64x2_t) -> uint64x2_t {
269 vcgtq_s64(a, b)
270 }
271
272 #[inline(always)]
273 unsafe fn _mm_cmplt(a: int64x2_t, b: int64x2_t) -> uint64x2_t {
274 vcltq_s64(a, b)
275 }
276
277 #[inline(always)]
278 unsafe fn _mm_blendv(a: int64x2_t, b: int64x2_t, mask: uint64x2_t) -> int64x2_t {
279 vbslq_s64(mask, b, a)
280 }
281 }
282
283 impl_SIMDInit_Int!(i64, int64x2_t, uint64x2_t, LANE_SIZE, NEON<Int>);
284
285 impl_SIMDArgMinMax!(
286 i64,
287 int64x2_t,
288 uint64x2_t,
289 LANE_SIZE,
290 SCALAR<Int>,
291 NEON<Int>,
292 "neon"
293 );
294}
295
296#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
299#[cfg(test)]
300mod tests {
301 use rstest::rstest;
302 use rstest_reuse::{self, *};
303 use std::marker::PhantomData;
304
305 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
306 #[cfg(feature = "nightly_simd")]
307 use crate::simd::config::AVX512;
308 #[cfg(target_arch = "aarch64")]
309 use crate::simd::config::NEON;
310 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
311 use crate::simd::config::{AVX2, SSE};
312 use crate::{Int, SIMDArgMinMax, SCALAR};
313
314 use super::super::test_utils::{
315 test_first_index_identical_values_argminmax, test_return_same_result_argminmax,
316 };
317
318 use dev_utils::utils;
319
320 fn get_array_i64(n: usize) -> Vec<i64> {
321 utils::SampleUniformFullRange::get_random_array(n)
322 }
323
324 const SCALAR_STRATEGY: SCALAR<Int> = SCALAR {
326 _dtype_strategy: PhantomData::<Int>,
327 };
328
329 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
332 #[template]
333 #[rstest]
334 #[case::sse(SSE {_dtype_strategy: PhantomData::<Int>}, is_x86_feature_detected!("sse4.2"))]
335 #[case::avx2(AVX2 {_dtype_strategy: PhantomData::<Int>}, is_x86_feature_detected!("avx2"))]
336 #[cfg_attr(feature = "nightly_simd", case::avx512(AVX512 {_dtype_strategy: PhantomData::<Int>}, is_x86_feature_detected!("avx512f")))]
337 fn simd_implementations<T, SIMDV, SIMDM, const LANE_SIZE: usize>(
338 #[case] simd: T,
339 #[case] simd_available: bool,
340 ) {
341 }
342
343 #[cfg(target_arch = "aarch64")]
346 #[template]
347 #[rstest]
348 #[case::neon(NEON {_dtype_strategy: PhantomData::<Int>}, true)]
349 fn simd_implementations<T, SIMDV, SIMDM, const LANE_SIZE: usize>(
350 #[case] simd: T,
351 #[case] simd_available: bool,
352 ) {
353 }
354
355 #[apply(simd_implementations)]
358 fn test_first_index_is_returned_when_identical_values_found<
359 T,
360 SIMDV,
361 SIMDM,
362 const LANE_SIZE: usize,
363 >(
364 #[case] simd: T,
365 #[case] simd_available: bool,
366 ) where
367 T: SIMDArgMinMax<i64, SIMDV, SIMDM, LANE_SIZE, SCALAR<Int>>,
368 SIMDV: Copy,
369 SIMDM: Copy,
370 {
371 if !simd_available {
372 return;
373 }
374 test_first_index_identical_values_argminmax(SCALAR_STRATEGY, simd);
375 }
376
377 #[apply(simd_implementations)]
378 fn test_return_same_result<T, SIMDV, SIMDM, const LANE_SIZE: usize>(
379 #[case] simd: T,
380 #[case] simd_available: bool,
381 ) where
382 T: SIMDArgMinMax<i64, SIMDV, SIMDM, LANE_SIZE, SCALAR<Int>>,
383 SIMDV: Copy,
384 SIMDM: Copy,
385 {
386 if !simd_available {
387 return;
388 }
389 test_return_same_result_argminmax(get_array_i64, SCALAR_STRATEGY, simd);
390 }
391}