#simd #stable #channel #work #simulated #effort #f64x2

ssimd

稳定通道的模拟 SIMD。使 simd 包在稳定通道上工作的努力

1 个不稳定版本

使用旧 Rust 2015

0.1.0 2017年11月22日

#1288 in 硬件支持

MIT/Apache

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)

所以所有操作都将自动为您完成。

无运行时依赖