Я пытаюсь создать эффективную SIMD-версию точечного продукта для реализации 2D-свертки для типа i16 для FIR-фильтра.
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
#[target_feature(enable = "avx2")]
unsafe fn dot_product(a: &[i16], b: &[i16]) {
let a = a.as_ptr() as *const [i16; 16];
let b = b.as_ptr() as *const [i16; 16];
let a = std::mem::transmute(*a);
let b = std::mem::transmute(*b);
let ms_256 = _mm256_mullo_epi16(a, b);
dbg!(std::mem::transmute::<_, [i16; 16]>(ms_256));
let hi_128 = _mm256_castsi256_si128(ms_256);
let lo_128 = _mm256_extracti128_si256(ms_256, 1);
dbg!(std::mem::transmute::<_, [i16; 8]>(hi_128));
dbg!(std::mem::transmute::<_, [i16; 8]>(lo_128));
let temp = _mm_add_epi16(hi_128, lo_128);
}
fn main() {
let a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
unsafe {
dot_product(&a, &b);
}
}
I] ~/c/simd (master|…) $ env RUSTFLAGS="-C target-cpu=native" cargo run --release | wl-copy
warning: unused variable: `temp`
--> src/main.rs:16:9
|
16 | let temp = _mm_add_epi16(hi_128, lo_128);
| ^^^^ help: if this is intentional, prefix it with an underscore: `_temp`
|
= note: `#[warn(unused_variables)]` on by default
warning: 1 warning emitted
Finished release [optimized] target(s) in 0.00s
Running `target/release/simd`
[src/main.rs:11] std::mem::transmute::<_, [i16; 16]>(ms_256) = [
0,
1,
4,
9,
16,
25,
36,
49,
64,
81,
100,
121,
144,
169,
196,
225,
]
[src/main.rs:14] std::mem::transmute::<_, [i16; 8]>(hi_128) = [
0,
1,
4,
9,
16,
25,
36,
49,
]
[src/main.rs:15] std::mem::transmute::<_, [i16; 8]>(lo_128) = [
64,
81,
100,
121,
144,
169,
196,
225,
]
Хотя я концептуально понимаю SIMD, я не знаком с точными инструкциями и внутренними функциями.
Я знаю, что мне нужно, чтобы умножить два вектора, а затем суммировать их по горизонтали, разделив их пополам и используя инструкции для вертикального добавления двух половин меньшего размера.
Я нашел инструкцию madd, которая предположительно должна сделать одно такое суммирование сразу после умножения, но не знаю, что делать с результатом.
Если использовать mul вместо madd, я не уверен, какие инструкции использовать, чтобы еще больше уменьшить результат.
Любая помощь приветствуется!
PS Я пробовал pack_simd но вроде не работает на стабильной ржавчине.
vpmaddwd
в векторный аккумулятор с_mm256_add_epi32
, и только последнее добавление по горизонтали (одного или пары векторов) в конце. (См. Самый быстрый способ выполнить горизонтальную векторную сумму SSE (или другое сокращение), предполагая, что вы можете адаптировать встроенные функции C / C ++ к Rust). Если вам нужно избежать переполнения 32-битных сумм, вам нужно снова расширить послеpmaddwd
внутри цикла. - person Peter Cordes   schedule 01.02.2021vpmaddwd
до тех пор, пока у вас один вектор, потом по горизонтали уменьшаем. Или, если вам нужно избежать переполнения в 32-битных суммах 16-битных продуктов, в какой-то момент расширьте их до 64-битных. - person Peter Cordes   schedule 01.02.2021as *const [i16; 16];
это говорит, что это вектор или 16 16-битных целых чисел. Я не уверен, почему для горизонтального суммирования 16-битного вектора мне понадобится более 1 вектора SIMD. Не могли бы вы уточнить? Может, напишем код на площадке для ржавчины? - person user1685095   schedule 01.02.2021-O3
и некоторой целевой спецификацией (-march=native
при сборке для вашей локальной машины), и вы получите довольно приличную сборку: godbolt.org/z/zdEoz7. Вы можете найти соответствующую встроенную функцию для каждой сгенерированной инструкции, если хотите преобразовать ее во встроенную функцию. - person chtz   schedule 01.02.2021a
иb
вi32
перед умножением, вы можете даже получитьvpmaddwd
(я не знаю достаточно ржавчины, чтобы попробовать это ...) - person chtz   schedule 01.02.2021