1 个不稳定版本
使用旧 Rust 2015
0.1.0 | 2017年11月22日 |
---|
#1288 in 硬件支持
30KB
507 行
ssimd
在 Rust 稳定通道上的模拟 SIMD。这是我为使 simd crate 在稳定通道上工作所做出的努力。这项工作基于众所周知的方法:自动向量化。然而,在这个 crate 中,我尝试提供一个尽可能接近 simd crate 的 API。虽然自动向量化似乎是一个运气问题,但通过以下简单技巧,我在大多数情况下成功实现了自动向量化。
注意
为了使大多数情况下的自动向量化成功,请启用 BB 优化器
https://llvm.net.cn/docs/Vectorizers.html#the-slp-vectorizer
对于 Rust,您可以通过以下构建命令构建项目来启用 BB 优化器
RUSTFLAGS="-C llvm-args=-vectorize-slp-aggressive" 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 crate 端口移植到稳定通道的。几乎对原始代码没有进行修改。对于这些示例,一些可能无法使用默认构建命令自动向量化。然而,当启用 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)
所以所有操作都将自动为您完成。