Я действительно могу подтвердить, что этот код вызывает отключение 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