关于连续迭代器的SIMD指令。

问题描述 投票:2回答:1

我有两个向量 v1v2 类型 T 并想创建一个函数来执行 v1 & v2 使用SIMD指令,并将输出存储在一个向量中。out.

理想的情况是,我们将有

first1  = v1.begin();
last1   = v1.end();
first2  = v2.begin();
d_first = out.begin();
while(distance(first1, last1) >= 64 / sizeof(T)) {
     *d_first = _mm512_and_epi32(first1, first2);
     first1   += 64 / sizeof(T)
     first2   += 64 / sizeof(T)
     d_first1 += 64 / sizeof(T)
}
auto and_op = [](T a, T b) {return a & b;};
std::transform(first1, last1, first2, d_first, and_op);

上面的代码的第一个问题是,它可以用32位的整数。我不确定它是否期望这些整数是对齐的,如果是,那么代码就不能工作,如果 T 是这样的 charshort int.

第二个问题是我不能让向量迭代器正确地投递。_mm512_and_epi32 期待两个 __m512i 变量作为输入。每当我传递一个连续的迭代器或地址时,编译器总是抱怨说没有从我传递的内容转换到"'__m512i'(8个'long长'值的向量)"

我可以通过以下方式让它工作。

__m512i _a = _mm512_load_epi64(&*first1.base());
__m512i _b = _mm512_load_epi64(&*first2.base());'
__m512i _res = _mm512_and_epi64(_a, _b);
_mm512_store_epi64(&*d_first.base(), _res);

但我不知道loadstore操作的成本有多高,也不知道是否可以跳过。

在大型连续数组上运行SIMD指令的正确方法是什么?有没有办法让它适用于所有类型的连续数组,不管它们的对齐方式如何?

c++ iterator sse simd intrinsics
1个回答
1
投票

通常情况下,你只需要从 .data() 容器上,并在数组上手动循环,就像一个C风格的数组。 或者递增一个索引,然后做 _mm512_loadu_si512(&vec[i]). (除非你使用了一个自定义对齐的分配器,用于你的 std::vector你不应该假设数据是对齐的。 但是在当前的HW上,512位向量可以从确保数据对齐中获得很大的好处,比如可能有20%的数据是对齐的,而256位向量只有几%)。)

你的dereferenced迭代器方式可能是安全的,如果能保证它是对底层数组元素的引用,而不是标量临时的。

Loadstore的内在并不比通过dereferencing something从内存中隐式加载的成本高,你需要从asm的角度去思考,才能理解成本。 编译器必须发出向量加载指令(或ALU指令的内存源操作数),以及存储指令,才能做出对内存中数据进行操作的asm。 _mm_load_si128 vs._mm_loadu_si128 基本上只是为了向编译器传达对齐信息和投递而存在。 并表达对其他C类型的严格的aliasing和对齐安全访问,比如memcpy。

© www.soinside.com 2019 - 2024. All rights reserved.