2 个版本
0.1.1 | 2023年4月10日 |
---|---|
0.1.0 | 2023年4月10日 |
295 在 过程宏
每月111 次下载
26KB
421 行
更便捷的 #[target_feature]
替代方案
为了从SIMD中获取良好的性能,SIMD代码路径上的所有内容都必须内联。在Rust中当前实现的SIMD中,对于使用SIMD的函数来说,必须满足以下两个条件之一才能进行内联:(这包括SIMD内建函数本身)
a) 整个程序必须使用相关的 -C target-cpu
或 -C target-feature
标志进行编译。
b) SIMD支持必须在运行时自动检测,并且SIMD代码路径上的每个函数都必须带有 #[target_feature]
属性。
两者都有其缺点。设置 target-cpu
或 target-features
使得生成的二进制文件与旧CPU不兼容,而使用 #[target_feature]
则非常不便。
这个crate旨在使 #[target_feature]
的使用更顺畅。
#[target_feature]
的问题
当我们没有编译相关的 target-cpu
/target-feature
标志时,SIMD代码路径上的所有内容都必须带有 #[target_feature]
属性。当所有SIMD代码都封装在一个函数中时,这并不是问题,但一旦开始构建更复杂的抽象,使用起来就会变得痛苦。
-
它只能用于
不安全
函数,所以现在你的SIMD代码路径上的所有内容都必须是不安全
。从理论上讲这很好——这些函数需要在运行时存在相关的SIMD指令,所以在不进行检查的情况下调用它们显然是不安全的!但在实践中,这很少是您想要的。当您在SIMD代码上构建抽象时,通常希望假设在模块内部所有必要的SIMD指令都是可用的,并且您只想在第一次进入模块时在边界处进行检查。您不希望将
不安全
感染模块内的所有内容,因为您已经在模块的API边界处检查了此不变性。 -
它不能用于非
不安全
的特质方法。如果您正在实现一个特质,例如例如
std::ops::Add
,那么除非原始特质也将其标记为不安全
,否则您不能标记该方法为不安全
,通常它不会。 -
它使得无法通过特质抽象特定的SIMD指令集。
例如,假设您想以以下方式使用特质抽象您使用的SIMD指令
trait Backend { unsafe fn sum(input: &[u32]) -> u32; } struct AVX; impl Backend for AVX { #[target_feature(enable = "avx")] unsafe fn sum(xs: &[u32]) -> u32 { // ... todo!(); } } struct AVX2; impl Backend for AVX2 { #[target_feature(enable = "avx2")] unsafe fn sum(xs: &[u32]) -> u32 { // ... todo!(); } } // And now you want a have function which calls into that trait: unsafe fn do_calculations<B>(xs: &[u32]) -> u32 where B: Backend { let value = B::sum(xs); // ...do some more calculations here... value }
这里有一个问题。这必须标记为
#[target_feature]
,并且必须指定给定SIMD指令集的具体功能标志,但这个函数是泛型的,所以我们不能这样做!
这个crate如何改进这一点?
现在您可以使用#[target_feature]
来标记安全函数
这个crate公开了一个#[unsafe_target_feature]
宏,它的工作方式与#[target_feature]
一样,但它将不安全
从函数原型移动到宏名称中,并且可以用于安全函数。
// ERROR: `#[target_feature(..)]` can only be applied to `unsafe` functions
#[target_feature(enable = "avx2")]
fn func() {}
// It works, but must be `unsafe`
#[target_feature(enable = "avx2")]
unsafe fn func() {}
use unsafe_target_feature::unsafe_target_feature;
// No `unsafe` on the function itself!
#[unsafe_target_feature("avx2")]
fn func() {}
它还可以用于标记impl中的函数
struct S;
impl core::ops::Add for S {
type Output = S;
// ERROR: method `add` has an incompatible type for trait
#[target_feature(enable = "avx2")]
unsafe fn add(self, rhs: S) -> S {
S
}
}
use unsafe_target_feature::unsafe_target_feature;
struct S;
#[unsafe_target_feature("avx2")]
impl core::ops::Add for S {
type Output = S;
// No `unsafe` on the function itself!
fn add(self, rhs: S) -> S {
S
}
}
您可以为每个目标功能生成模块的专用副本
use unsafe_target_feature::unsafe_target_feature_specialize;
#[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", nightly))]
mod simd {
#[for_target_feature("sse2")]
pub const CONSTANT: u32 = 1;
#[for_target_feature("avx2")]
pub const CONSTANT: u32 = 2;
#[for_target_feature("avx512ifma")]
pub const CONSTANT: u32 = 3;
pub fn func() { /* ... */ }
}
fn entry_point() {
#[cfg(nightly)]
if std::is_x86_feature_detected!("avx512ifma") {
return simd_avx512ifma::func();
}
if std::is_x86_feature_detected!("avx2") {
return simd_avx2::func();
}
if std::is_x86_feature_detected!("sse2") {
return simd_sse2::func();
}
unimplemented!();
}
如何使用#[unsafe_target_feature]
?
- 可以用于
fn
、impl
和mod
。 - 当用于函数时,只会应用于该函数;它不会应用于任何嵌套函数、特质、mods等。
- 当用于
impl
时,将只应用于该impl中直接定义的所有函数。 - 当用于
mod
时,将仅适用于该mod
内部直接定义的所有fn
和impl
。 - 不能用于使用
self
或Self
的方法;相反,应在定义该方法的impl
中使用。
许可证
根据您的选择,许可协议为以下之一
- Apache License,版本 2.0,(LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
。
贡献
除非您明确说明,否则您有意提交并包含在本作品中的任何贡献(根据 Apache-2.0 许可证定义),将根据上述协议双许可,不附加任何额外条款或条件。
依赖项
~285–740KB
~18K SLoC