Наистина мога да потвърдя, че този код кара assert да се задейства в режим на освобождаване:
$ cargo run --release
Finished release [optimized] target(s) in 0.00s
Running `target/release/so53831502`
ar: __m256(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
br: __m256(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
abr: __m256(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
abr: __m256(-1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0)
ab: [-1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0]
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `-1.0`,
right: `-2.0`', src/main.rs:24:9
Това изглежда е грешка в компилатора, вижте тук и тук. По-специално, вие извиквате процедури като _mm256_set1_ps
и _mm256_fmadd_ps
, които изискват функциите на процесора avx
и fma
съответно, но нито вашият код, нито вашата команда за компилиране показват на компилатора, че трябва да се използват такива функции.
Един от начините за коригиране на това е да кажете на компилатора да компилира цялата програма с активирани функции avx
и fma
, ето така:
$ RUSTFLAGS="-C target-feature=+avx,+fma" cargo run --release
Compiling so53831502 v0.1.0 (/tmp/so53831502)
Finished release [optimized] target(s) in 0.36s
Running `target/release/so53831502`
ar: __m256(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
br: __m256(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
abr: __m256(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
abr: __m256(-2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
ab: [-2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
Друг подход, който постига същия резултат, е да кажете на компилатора да използва всички налични функции на процесора на вашия процесор:
$ RUSTFLAGS="-C target-cpu=native" cargo run --release
Compiling so53831502 v0.1.0 (/tmp/so53831502)
Finished release [optimized] target(s) in 0.34s
Running `target/release/so53831502`
ar: __m256(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
br: __m256(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
abr: __m256(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
abr: __m256(-2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
ab: [-2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
Въпреки това и двете команди за компилиране създават двоични файлове, които могат да работят само на процесори, които поддържат функциите avx
и fma
. Ако това не е проблем за вас, тогава това е добро решение. Ако вместо това искате да изградите преносими двоични файлове, тогава можете да извършите откриване на функции на процесора по време на изпълнение и да компилирате определени функции с активирани специфични функции на процесора. Тогава е ваша отговорност да гарантирате, че споменатите функции се извикват само когато съответната функция на процесора е активирана и налична. Този процес е документиран като част от динамичния процесор раздел за откриване на функции на std::arch
документите.
Ето пример, който използва откриване на функция на процесора по време на изпълнение:
use std::arch::x86_64::*;
use std::process;
fn main() {
if is_x86_feature_detected!("avx") && is_x86_feature_detected!("fma") {
// SAFETY: This is safe because we're guaranteed to support the
// necessary CPU features.
unsafe { doit(); }
} else {
eprintln!("unsupported CPU");
process::exit(1);
}
}
#[target_feature(enable = "avx,fma")]
unsafe fn doit() {
let a = vec![2.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let b = -1.0f32;
let ar = _mm256_loadu_ps(a.as_ptr());
println!("ar: {:?}", ar);
let br = _mm256_set1_ps(b);
println!("br: {:?}", br);
let mut abr = _mm256_setzero_ps();
println!("abr: {:?}", abr);
abr = _mm256_fmadd_ps(ar, br, abr);
println!("abr: {:?}", abr);
let mut ab = [0.0; 8];
_mm256_storeu_ps(ab.as_mut_ptr(), abr);
println!("ab: {:?}", ab);
assert_eq!(ab[0], -2.0f32);
}
За да го стартирате, вече не е необходимо да задавате флагове за компилация:
$ cargo run --release
Compiling so53831502 v0.1.0 (/tmp/so53831502)
Finished release [optimized] target(s) in 0.29s
Running `target/release/so53831502`
ar: __m256(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
br: __m256(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
abr: __m256(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
abr: __m256(-2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
ab: [-2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
Ако стартирате получения двоичен файл на процесор, който не поддържа нито avx
, нито fma
, тогава програмата трябва да излезе със съобщение за грешка: unsupported CPU
.
Като цяло мисля, че документите за std::arch
могат да бъдат подобрени. По-специално, ключовата граница, на която трябва да разделите кода си, зависи от това дали вашите векторни типове се появяват в сигнатурата на вашата функция. Това означава, че рутината doit
не изисква нищо извън стандартната x86 (или x86_64) функция ABI за извикване и следователно е безопасно да се извиква от функции, които иначе не поддържат avx
или fma
. Въпреки това, вътрешно на функцията е казано да компилира кода си, като използва допълнителни разширения на набор от инструкции, базирани на дадените функции на процесора. Това се постига чрез атрибута target_feature
. Ако например сте предоставили неправилна целева функция:
#[target_feature(enable = "ssse3")]
unsafe fn doit() {
// ...
}
тогава програмата проявява същото поведение като вашата първоначална програма.
person
BurntSushi5
schedule
18.12.2018
_mm256_fmadd_ps
в версиите на версията. - person Peter Hall   schedule 18.12.2018