1 个不稳定版本
0.0.1 | 2020年4月16日 |
---|
#5 在 #模拟
36KB
639 行
ssimd
这是对未维护的 ssimd 包的分支,目标是使其与 packed_simd 兼容。
在Rust稳定通道上的模拟SIMD。这是使 packed-simd 包 在稳定通道上工作的努力。这项工作基于众所周知的方法:自动向量化。然而,在这个包中,我试图提供一个尽可能接近 simd 包的API。虽然自动向量化似乎是一个运气,但通过以下简单技巧,我已经在大多数情况下使自动向量化成功。
注意
为了使大多数情况下自动向量化成功,请启用BB优化器
https://llvm.net.cn/docs/Vectorizers.html#the-slp-vectorizer
对于Rust,您可以通过以下构建命令构建项目来启用BB优化器
RUSTFLAGS="-C llvm-args=--vectorize-slp -C target-cpu=native" cargo build --release
示例
让我们从一个非常简单的例子开始
extern crate ssimd;
use ssimd::f64x2;
#[inline(never)]
fn test_simd(a : f64x2, b: f64x2) {
let c = a + b;
println!("{:?}", c);
}
fn main() {
let a = f64x2::new(1.0, 1.0);
let b = f64x2::new(2.0, 3.0);
test_simd(a, b);
}
当编译为llvm-ir代码时,您将在函数 "test_simd" 中看到这些指令
%7 = fadd <2 x double> %4, %6
store <2 x double> %7, <2 x double>* %c, align 16
以及等效的汇编代码
addpd %xmm0, %xmm2
movapd %xmm2, 16(%rsp)
在这种情况下,即使使用默认的构建命令,LLVM也可以成功向量化代码。
让我们尝试一个更复杂的例子
extern crate ssimd;
use std::io::{self, BufRead};
use ssimd::f64x2;
fn test_simd(i : i32) {
let a = f64x2::new((i + 1) as f64, (i + 2) as f64);
let b = f64x2::new((i + 3) as f64, (i + 3) as f64);
let c = a + b;
println!("{:?}", c);
}
fn main() {
let stdin = io::stdin();
let mut line = String::new();
stdin.lock().read_line(&mut line).unwrap();
let i : i32 = line.trim().parse().unwrap_or(0);
test_simd(i);
}
函数 "test_simd" 中的 llvm-ir 代码
%104 = bitcast %"ssimd::f64x2"* %c.i to i8*
call void @llvm.lifetime.start(i64 16, i8* nonnull %104)
%105 = fadd double %99, %103
%106 = fadd double %101, %103
以及汇编代码
cvtsi2sdl %ecx, %xmm2
addsd %xmm2, %xmm0
addsd %xmm2, %xmm1
所以当在函数内部插入整数到浮点转换时,LLVM无法向量化代码。但是,如果启用BB优化器,您将看到以下 llvm-ir 代码
%109 = fadd <2 x double> %108, %106
store <2 x double> %109, <2 x double>* %c.i, align 16
以及汇编代码
addpd %xmm2, %xmm0
movapd %xmm0, 96(%rsp)
因此代码成功向量化。
您可以在 "examples" 文件夹中看到更多示例。这些示例是从 simd 包 移植到稳定通道的。几乎对原始代码没有进行修改。对于这些示例,有些可能在默认构建命令下无法自动向量化。然而,当启用BB优化器时,所有示例都成功向量化。您可以用自己的示例尝试。
AVX指令
某些机器上不支持AVX指令。如果您想使用内联方法(例如在simd crate中)使用AVX指令,您需要使用Rust的"target_feature"属性来检测机器是否支持这些指令。您还需要提供一个后备函数方法,以防AVX指令不可用。然而,如果您使用自动向量化,您只需要提供一个函数方法,因为LLVM将为每个机器配置生成适当的指令。
考虑以下示例
extern crate ssimd;
use ssimd::f64x4;
#[inline(never)]
fn test_simd(a : f64x4, b: f64x4) {
let c = a + b;
println!("{:?}", c);
}
fn main() {
let a = f64x4::new(1.0, 1.0, 1.0, 1.0);
let b = f64x4::new(2.0, 3.0, 4.0, 5.0);
test_simd(a, b);
}
在没有AVX指令的机器上,LLVM将生成以下代码
addpd %xmm0, %xmm2
addpd %xmm1, %xmm3
movapd %xmm2, 64(%rsp)
movapd %xmm3, 80(%rsp)
在支持AVX指令的机器上,LLVM将生成以下代码
vaddpd (%rsi), %ymm0, %ymm0
vmovapd %ymm0, 64(%rsp)
所以所有的工作都将自动为您完成。