我将内核分解为几个循环,以便之后对每个循环进行矢量化。其中一个循环看起来像:
int *array1; //Its size is "size+1";
int *array2; //Its size is "size+1";
//All positions of array1 and array2 are set to 0 here;
int *sArray1 = array1+1; //Shift one position so I start writing on pos 1
int *sArray2 = array2+1; //Shift one position so I start writing on pos 1
int bb = 0;
for(int i=0; i<size; i++){
if(A[i] + bb > B[i]){
bb = 1;
sArray1[i] = S;
sArray2[i] = 1;
}
else
bb = 0;
}
请注意bb
中的循环携带依赖关系 - 每次比较都取决于bb
的值,该值在前一次迭代中被修改。
我的想法:
A[i]
已经大于B[i]
时,我不需要知道前一次迭代带有的值bb
; A[i]
等于B[i]
时,我需要知道前一次迭代的值bb
。但是,我还需要考虑这种情况发生在两个连续的位置;当我开始塑造这些案例时,似乎这些案件变得过于复杂,而且矢量化并没有带来回报。基本上,我想知道这是否可以以有效的方式进行矢量化,或者如果没有任何矢量化就可以更好地运行它。
答案 0 :(得分:0)
您可能不希望迭代单个元素,但在块上有一个循环(其中一个块由产生相同bb
的所有元素定义。)
可以对源块的搜索进行矢量化(可能使用编译器特定的SIMD内在函数手动)。 并且对bb = 1的单个块采取的动作也可以被矢量化。 循环转换如下:
size_t i_chunk_start = 0, i_chunk_end;
int bb_chunk = A[0] > B[0] ? 1 : 0;
while (i_chunk_start < isize) {
if(bb_chunk) {
/* find end of current chunk */
for (i_chunk_end = i_chunk_start + 1; i_chunk_end < isize; ++i_chunk_end) {
if(A[i_chunk_end] < B[i_chunk_end]) {
break;
}
}
/* process current chunk */
for(size_t i = i_chunk_start; i < i_chunk_end; ++i) {
sArray1[i] = S;
sArray2[i] = 1;
}
bb_chunk = 0;
} else {
/* find end of current chunk */
for (i_chunk_end = i_chunk_start + 1; i_chunk_end < isize; ++i_chunk_end) {
if(A[i_chunk_end] > B[i_chunk_end]) {
break;
}
}
bb_chunk = 1;
}
/* prepare for next chunk */
i_chunk_start = i_chunk_end;
}
现在,每个内部循环(所有for循环)都可能被矢量化。
这种方式的矢量化是否优于非矢量化取决于块是否具有足够的平均长度。您只能通过基准测试找到。
答案 1 :(得分:0)
你的循环体的效果取决于两个条件:
A[i] > B[i]
A[i] + 1 > B[i]
他们的计算可以很容易地进行矢量化。假设int
有32位,并且向量化指令一次处理4 int
个值,每个向量化迭代有8位(每个条件4位)。
您可以通过_mm_movemask_epi8
从SSE寄存器中获取这些位。这有点不方便,它在字节而不是int
上工作,但你可以通过合适的随机播放来处理它。
然后,使用8位作为LUT(256个条目)的地址,该LUT存储4位掩码。这些掩码可用于使用_mm_maskmoveu_si128
有条件地将元素存储到目标中。
我不确定这样一个复杂的程序是否值得 - 它只需要很快就能提高x4的速度。也许最好通过分别检查决策位来构建掩码。但无论如何,矢量化你的比较和商店似乎都是值得的。