GCC C 向量扩展:如何将向量的内容向左移动一个元素?
GCC C vector extension: How to move contents of a vector to the left by one element?
我不熟悉 GCC 的 C 向量扩展。我正在考虑在我的项目中使用它们,但它们的效用(在某种程度上)取决于有效地将向量中的所有元素向左移动一个位置并将结果存储在新向量中的能力。我怎样才能有效地做到这一点(例如以 SIMD 加速的方式)?
所以,基本上:
- 原始向量 = {1, 2, 3, 4, 5, 6, 7, 8}
- ShiftedVector = {2, 3, 4, 5, 6, 7, 8, X}
(其中 X 可以是任何东西。)
背景信息(你可以跳过):这种转换的目的是处理每行都用向量表示的矩阵。具体来说,它将使人们能够将 ShiftedVector 视为下方行的左上角对角线,并在一个 SIMD 操作中比较所有值。如果有另一种方法可以将一个向量与偏移一个元素的另一个向量进行比较,那也可以解决问题。但我假设不是,执行此比较的最有效方法是将所有元素向左移动并进行比较 1:1.
一般规定:
- 在此过程中不得损坏原始向量
- 如果我必须使用某种 x86 intrinsic function 没问题,但我不知道使用哪种或如何使用
- 如果我丢失向量中最左边的元素并在最右边引入乱码就没问题了
- 如果最有效的方法是从第二个位置到 end+1 的原始向量的未对齐加载,那很好,但我仍然想知道如何最好地编写此代码
这里的瓶颈似乎是缺乏关于使用内在函数过程的一般信息。似乎人们正在使用汇编(我不是专家)或自动矢量化(doesn't work well here),因此矢量类型是最合乎逻辑的选择。
谢谢!
最快的转变是完全不转变(即不移动,不复制):
int Data[16] = {
1, 2, 3, 4, 5, 6, 7, 8,
0, 0, 0, 0, 0, 0, 0, 0,
};
int* Ptr = Data;
// first shift
Ptr++;
// second shift
Ptr++;
// and so on.
如果算法允许(即移位次数有限且提前已知),则可以保留足够的space,并通过递增指针使"shifts"。
在手册的深处爬来爬去,我发现了这个愚蠢的地方:
typedef int v8si __attribute__ ((vector_size (32)));
v8si OriginalVector, masker, ShiftedVector;
OriginalVector = {1, 2, 3, 4, 5, 6, 7, 8};
masker = {1,2,3,4,5,6,7,0};
ShiftedVector = __builtin_shuffle(OriginalVector, masker);
我无缘无故在 "masker" 末尾放了一个 0(任何元素 0-7 都可以)。它所做的只是将原始中的元素映射到masker中定义的位置,并将它们保存到结果中。
但是虽然这是一个答案,但它可能不是 "best" 答案,因为我想有比创建一个新向量更好的方法,用新向量占用一个寄存器,分配位置,将每个元素移到另一个任意位置,然后保存结果。
是的,我们可以在循环之外缓存屏蔽器或其他东西,而不是每次都创建它,但我想在某处有一些简单的 "permute left" 指令可以将它滑过...
我不熟悉 GCC 的 C 向量扩展。我正在考虑在我的项目中使用它们,但它们的效用(在某种程度上)取决于有效地将向量中的所有元素向左移动一个位置并将结果存储在新向量中的能力。我怎样才能有效地做到这一点(例如以 SIMD 加速的方式)?
所以,基本上:
- 原始向量 = {1, 2, 3, 4, 5, 6, 7, 8}
- ShiftedVector = {2, 3, 4, 5, 6, 7, 8, X} (其中 X 可以是任何东西。)
背景信息(你可以跳过):这种转换的目的是处理每行都用向量表示的矩阵。具体来说,它将使人们能够将 ShiftedVector 视为下方行的左上角对角线,并在一个 SIMD 操作中比较所有值。如果有另一种方法可以将一个向量与偏移一个元素的另一个向量进行比较,那也可以解决问题。但我假设不是,执行此比较的最有效方法是将所有元素向左移动并进行比较 1:1.
一般规定:
- 在此过程中不得损坏原始向量
- 如果我必须使用某种 x86 intrinsic function 没问题,但我不知道使用哪种或如何使用
- 如果我丢失向量中最左边的元素并在最右边引入乱码就没问题了
- 如果最有效的方法是从第二个位置到 end+1 的原始向量的未对齐加载,那很好,但我仍然想知道如何最好地编写此代码
这里的瓶颈似乎是缺乏关于使用内在函数过程的一般信息。似乎人们正在使用汇编(我不是专家)或自动矢量化(doesn't work well here),因此矢量类型是最合乎逻辑的选择。
谢谢!
最快的转变是完全不转变(即不移动,不复制):
int Data[16] = {
1, 2, 3, 4, 5, 6, 7, 8,
0, 0, 0, 0, 0, 0, 0, 0,
};
int* Ptr = Data;
// first shift
Ptr++;
// second shift
Ptr++;
// and so on.
如果算法允许(即移位次数有限且提前已知),则可以保留足够的space,并通过递增指针使"shifts"。
在手册的深处爬来爬去,我发现了这个愚蠢的地方:
typedef int v8si __attribute__ ((vector_size (32)));
v8si OriginalVector, masker, ShiftedVector;
OriginalVector = {1, 2, 3, 4, 5, 6, 7, 8};
masker = {1,2,3,4,5,6,7,0};
ShiftedVector = __builtin_shuffle(OriginalVector, masker);
我无缘无故在 "masker" 末尾放了一个 0(任何元素 0-7 都可以)。它所做的只是将原始中的元素映射到masker中定义的位置,并将它们保存到结果中。
但是虽然这是一个答案,但它可能不是 "best" 答案,因为我想有比创建一个新向量更好的方法,用新向量占用一个寄存器,分配位置,将每个元素移到另一个任意位置,然后保存结果。
是的,我们可以在循环之外缓存屏蔽器或其他东西,而不是每次都创建它,但我想在某处有一些简单的 "permute left" 指令可以将它滑过...