substrate_data/wire/frame/lanes/
backend.rs1use std::fmt;
5
6use super::{GROUP, group_bytes, portable};
7use crate::sync::OnceLock;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12#[non_exhaustive]
13pub enum Backend {
14 Portable,
16 Sse42,
18 Avx2,
20 Avx512,
22 Neon,
24}
25
26const BEST_FIRST: [Backend; 5] = [
28 Backend::Avx512,
29 Backend::Avx2,
30 Backend::Neon,
31 Backend::Sse42,
32 Backend::Portable,
33];
34
35impl Backend {
36 #[must_use]
39 pub fn active() -> Self {
40 static ACTIVE: OnceLock<Backend> = OnceLock::new();
41 *ACTIVE.get_or_init(|| {
42 BEST_FIRST
43 .into_iter()
44 .find(|b| b.is_available())
45 .unwrap_or(Self::Portable)
46 })
47 }
48
49 pub fn available() -> impl Iterator<Item = Self> {
51 BEST_FIRST.into_iter().filter(|b| b.is_available())
52 }
53
54 #[must_use]
56 pub fn is_available(self) -> bool {
57 match self {
58 Self::Portable => true,
59 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
60 Self::Sse42 => std::arch::is_x86_feature_detected!("sse4.2"),
61 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
62 Self::Avx2 => std::arch::is_x86_feature_detected!("avx2"),
63 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
64 Self::Avx512 => {
65 std::arch::is_x86_feature_detected!("avx512f")
66 && std::arch::is_x86_feature_detected!("avx2")
67 }
68 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
69 Self::Neon => std::arch::is_aarch64_feature_detected!("neon"),
70 _ => false,
71 }
72 }
73
74 pub fn pack_groups(self, src: &[[u32; GROUP]], width: u32, out: &mut [u8]) {
82 assert!(self.is_available(), "{self} is not available here");
83 assert!((1..=32).contains(&width), "a group is 1 to 32 bits wide");
84 let size = group_bytes(width);
85 assert!(out.len() >= src.len() * size, "a group's bytes");
86 let numbers = src.as_flattened();
87 let mut group = 0;
88 while group < src.len() {
89 let backend = self.for_groups(width, src.len() - group);
90 backend.pack_unchecked(&numbers[group * GROUP..], width, &mut out[group * size..]);
91 group += backend.groups_per_call();
92 }
93 }
94
95 pub fn unpack_groups(self, src: &[u8], width: u32, out: &mut [[u32; GROUP]]) {
101 assert!(self.is_available(), "{self} is not available here");
102 assert!((1..=32).contains(&width), "a group is 1 to 32 bits wide");
103 let size = group_bytes(width);
104 assert!(src.len() >= out.len() * size, "a group's bytes");
105 let count = out.len();
106 let numbers = out.as_flattened_mut();
107 let mut group = 0;
108 while group < count {
109 let backend = self.for_groups(width, count - group);
110 backend.unpack_unchecked(&src[group * size..], width, &mut numbers[group * GROUP..]);
111 group += backend.groups_per_call();
112 }
113 }
114
115 fn groups_per_call(self) -> usize {
117 match self {
118 Self::Avx512 => 2,
119 _ => 1,
120 }
121 }
122
123 fn for_groups(self, width: u32, left: usize) -> Self {
129 match self {
130 Self::Avx512 if left < self.groups_per_call() || width == u32::BITS => Self::Avx2,
131 other => other,
132 }
133 }
134
135 pub(super) fn pack_unchecked(self, src: &[u32], width: u32, out: &mut [u8]) {
138 match self {
139 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
140 Self::Sse42 => super::sse42::pack(src, width, out),
141 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
142 Self::Avx2 => super::avx2::pack(src, width, out),
143 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
144 Self::Avx512 => super::avx512::pack(src, width, out),
145 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
146 Self::Neon => super::neon::pack(src, width, out),
147 _ => portable::pack(src, width, out),
148 }
149 }
150
151 pub(super) fn unpack_unchecked(self, src: &[u8], width: u32, out: &mut [u32]) {
153 match self {
154 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
155 Self::Sse42 => super::sse42::unpack(src, width, out),
156 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
157 Self::Avx2 => super::avx2::unpack(src, width, out),
158 #[cfg(all(target_arch = "x86_64", target_endian = "little"))]
159 Self::Avx512 => super::avx512::unpack(src, width, out),
160 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
161 Self::Neon => super::neon::unpack(src, width, out),
162 _ => portable::unpack(src, width, out),
163 }
164 }
165}
166
167impl fmt::Display for Backend {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.write_str(match self {
170 Self::Portable => "portable",
171 Self::Sse42 => "SSE4.2",
172 Self::Avx2 => "AVX2",
173 Self::Avx512 => "AVX-512",
174 Self::Neon => "NEON",
175 })
176 }
177}