C++ 向量化嵌套循环-SIMD
是否有人知道使用SIMD对类似的内容进行矢量化:C++ 向量化嵌套循环-SIMD,c++,parallel-processing,x86,vectorization,simd,C++,Parallel Processing,X86,Vectorization,Simd,是否有人知道使用SIMD对类似的内容进行矢量化: for(size_t i = 0; i < refSeq.length() / 4; i++){ for(size_t j = 0; j<otherSeq.length(); j++){ if(refSeq[i] == otherSeq[j]){ if(i == 0 || j == 0) L[i][j] = 1; else L[i][j] = L[
for(size_t i = 0; i < refSeq.length() / 4; i++){
for(size_t j = 0; j<otherSeq.length(); j++){
if(refSeq[i] == otherSeq[j]){
if(i == 0 || j == 0)
L[i][j] = 1;
else
L[i][j] = L[i-1][j-1] + 1;
}
else
L[i][j] = 0;
}
}
(大小i=0;i{
对于(size_t j=0;j让我尝试提出一个解决方案。首先计算L[i][0]和L[0][j]的值。现在开始从i=1和j=1进行迭代。现在可以删除循环每次迭代中对i==0或j==0的检查。这样做的另一个优点是,对于行上每次迭代中的每个L[i][j],L[i-1][j-1]的值是可用的。现在,假设向量寄存器可以容纳数组的4个元素。现在我们可以加载refSeq、otherSeq、L(前一行)和L(当前行)的4个元素。理论上我们现在可以进行向量化。我假设自动向量器无法识别这一点。因此我们必须手动执行。如果我错了,请纠正我
for(size_t i=0;i<refSeq.length()/4;i++)
{
if(refSeq[i]==otherSeq[0])
L[i][0]=1;
else
L[i][0]=0;
}
for(size_t j=0; j<otherSeq.length();j++)
{
if(refSeq[0]==otherSeq[j])
L[0][j]=1;
else
L[0][j]=0;
}
for(size_t i=1;i<refSeq.length()/4;i++)
{
for(size_t j=1; j<otherSeq.length();j++)
{
if(refSeq[i]==otherSeq[j])
L[i][j] = L[i-1][j-1] + 1;
else
L[i][j]=0;
}
}
for(size_t i=0;i这是一个动态规划问题,Streat forward实现有太多的数据依赖性,不适合SIMD计算
但是,如果将算法从按行迭代改为按对角线迭代,则可以并行计算整个对角线。请参见下图
下面的“伪”代码使用了一个额外行/列的矩阵,以简化“内部”计算。该额外行/列在每次对角迭代之前初始化
int i, j, k;
for (k = 1; ; k++) {
int minI = k > refLen ? k - refLen : 1;
int maxI = k > otherLen ? otherLen : k - 1;
for (i = maxI; i >= minI; ) {
j = k - i;
// vectorized calculation 256 bit (AVX2)
if (i >= 32 && otherLen - j >= 32) {
// calculate 32 values of the diagonal with SIMD
i -= 32;
continue;
}
// vectorized calculation 128 bit (SSE)
if (i >= 16 && otherLen - j >= 16) {
// calculate 16 values of the diagonal with SIMD
i -= 16;
continue;
}
// scalar calculation
if (refSeq[i - 1] == otherSeq[j - 1]) {
L[i][j] = L[i - 1][j - 1] + 1;
} else {
L[i][j] = 0;
}
i--;
}
if (k == otherLen + refLen) {
break;
}
// initialize next j-endpoint in diagonal
if (k <= refLen) {
L[0][k] = 0;
}
// initialize next i-endpoint in diagonal
if (k <= otherLen) {
L[k][0] = 0;
}
}
inti,j,k;
对于(k=1;;k++){
int minI=k>refLen?k-refLen:1;
int maxI=k>otherLen?otherLen:k-1;
对于(i=maxI;i>=minI;){
j=k-i;
//矢量化计算256位(AVX2)
如果(i>=32&&otherLen-j>=32){
//使用SIMD计算对角线的32个值
i-=32;
继续;
}
//矢量化计算128位(SSE)
如果(i>=16&&otherLen-j>=16){
//使用SIMD计算对角线的16个值
i-=16;
继续;
}
//标量计算
if(refSeq[i-1]==otherSeq[j-1]){
L[i][j]=L[i-1][j-1]+1;
}否则{
L[i][j]=0;
}
我--;
}
if(k==otherLen+refLen){
打破
}
//初始化对角线中的下一个j端点
如果(k)当你说“SIMD”时,你需要指定你正在谈论的CPU系列,例如x86(在这种情况下,你需要指定可以假定的SSE/AVX级别)、PowerPC(AltiVec)、POWER(VMX/VSX)、ARM(Neon)、Cell等。另外,refSeq[]
、otherSeq[]
和L[]
?您确实非常坚持让这个最长的子字符串算法再次并行:)数据依赖性。SIMD工作在独立的数据块上。这里您有if
(坏)和if
(更糟)。您需要重新设计算法以使用掩蔽而不是分支,我不确定它是否会运行得更快。它适用于x86,所有这些都是字符串。SSE intel的指令。@Hristo是的,我尝试了所有方法,但我能找到的只是一些有趣的示例,没有更复杂的示例。:)是的,掩蔽…尽管有一个:)谢谢:)