我想用SIMD指令将下面的fortran矢量化
!DIR$ SIMD
DO IELEM = 1 , NELEM
X(IKLE(IELEM)) = X(IKLE(IELEM)) + W(IELEM)
ENDDO
我使用了avx2指令。这个程序是由
ifort main_vec.f -simd -g -pg -O2 -vec-report6 -o vec.out -xcore-avx2 -align array32byte
那么我想在VECTORLENGTH(n)
之后添加SIMD
子句。如果没有这样的子句或n= 2,4,则信息不会提供有关展开因子的信息。
如果n= 8,16,vectorization support: unroll factor set to 2
。
我读过英特尔关于矢量化支持:展开因子设置为xxxx的文章,所以我想循环是展开成这样的:
DO IELEM = 1 , NELEM, 2
X(IKLE(IELEM)) = X(IKLE(IELEM)) + W(IELEM)
X(IKLE(IELEM+1)) = X(IKLE(IELEM+1)) + W(IELEM+1)
ENDDO
然后2X进入向量寄存器,2W转到另一个,做加法。但是VECTORLENGTH的价值是如何工作的呢?或者我不太明白向量长度是什么意思。
由于我使用avx2指令,对于DOUBLE PRECISION
类型的X
,可以达到的最大长度是多少?
下面是包含SSE2、VL=8和编译器的循环汇编的一部分,它告诉我展开因子是2,但是它使用了4个寄存器而不是2个寄存器。
.loc 1 114 is_stmt 1
movslq main_vec_$IKLE.0.1(,%rdx,4), %rsi #114.9
..LN202:
movslq 4+main_vec_$IKLE.0.1(,%rdx,4), %rdi #114.9
..LN203:
movslq 8+main_vec_$IKLE.0.1(,%rdx,4), %r8 #114.9
..LN204:
movslq 12+main_vec_$IKLE.0.1(,%rdx,4), %r9 #114.9
..LN205:
movsd -8+main_vec_$X.0.1(,%rsi,8), %xmm0 #114.26
..LN206:
movslq 16+main_vec_$IKLE.0.1(,%rdx,4), %r10 #114.9
..LN207:
movhpd -8+main_vec_$X.0.1(,%rdi,8), %xmm0 #114.26
..LN208:
movslq 20+main_vec_$IKLE.0.1(,%rdx,4), %r11 #114.9
..LN209:
movsd -8+main_vec_$X.0.1(,%r8,8), %xmm1 #114.26
..LN210:
movslq 24+main_vec_$IKLE.0.1(,%rdx,4), %r14 #114.9
..LN211:
addpd main_vec_$W.0.1(,%rdx,8), %xmm0 #114.9
..LN212:
movhpd -8+main_vec_$X.0.1(,%r9,8), %xmm1 #114.26
..LN213:
..LN214:
movslq 28+main_vec_$IKLE.0.1(,%rdx,4), %r15 #114.9
..LN215:
movsd -8+main_vec_$X.0.1(,%r10,8), %xmm2 #114.26
..LN216:
addpd 16+main_vec_$W.0.1(,%rdx,8), %xmm1 #114.9
..LN217:
movhpd -8+main_vec_$X.0.1(,%r11,8), %xmm2 #114.26
..LN218:
..LN219:
movsd -8+main_vec_$X.0.1(,%r14,8), %xmm3 #114.26
..LN220:
addpd 32+main_vec_$W.0.1(,%rdx,8), %xmm2 #114.9
..LN221:
movhpd -8+main_vec_$X.0.1(,%r15,8), %xmm3 #114.26
..LN222:
..LN223:
addpd 48+main_vec_$W.0.1(,%rdx,8), %xmm3 #114.9
..LN224:
movsd %xmm0, -8+main_vec_$X.0.1(,%rsi,8) #114.9
..LN225:
.loc 1 113 is_stmt 1
addq $8, %rdx #113.7
..LN226:
.loc 1 114 is_stmt 1
psrldq $8, %xmm0 #114.9
..LN227:
.loc 1 113 is_stmt 1
cmpq $26000, %rdx #113.7
..LN228:
.loc 1 114 is_stmt 1
movsd %xmm0, -8+main_vec_$X.0.1(,%rdi,8) #114.9
..LN229:
movsd %xmm1, -8+main_vec_$X.0.1(,%r8,8) #114.9
..LN230:
psrldq $8, %xmm1 #114.9
..LN231:
movsd %xmm1, -8+main_vec_$X.0.1(,%r9,8) #114.9
..LN232:
movsd %xmm2, -8+main_vec_$X.0.1(,%r10,8) #114.9
..LN233:
psrldq $8, %xmm2 #114.9
..LN234:
movsd %xmm2, -8+main_vec_$X.0.1(,%r11,8) #114.9
..LN235:
movsd %xmm3, -8+main_vec_$X.0.1(,%r14,8) #114.9
..LN236:
psrldq $8, %xmm3 #114.9
..LN237:
movsd %xmm3, -8+main_vec_$X.0.1(,%r15,8) #114.9
..LN238:
发布于 2015-08-19 01:18:02
1) 向量长度N是在循环“向量化”后可以并行执行的许多元素/迭代(通常通过将X阵列的N个元素放入单个向量寄存器并通过向量指令进行处理)。为了简化,可以把向量长度看作这个公式给出的值:
Vector Length (abbreviated VL) = Vector Register Width / Sizeof (data type)
对于AVX2,矢量寄存器宽度= 256位。Sizeof (双精度)=8字节= 64位。因此:
Vector Length (double FP, avx2) = 256 / 64 = 4
$DIR SIMD VECTORLENGTH (N)基本上强制编译器使用指定的向量长度(并将X数组的N个元素放入单矢量寄存器)。就这样。
2) 展开与矢量化关系。为了简化,可以将展开和矢量化看作是通常的无关(有点“正交”)优化技术。
如果您的循环按M的因子展开(M可以是2,4,..),那么它不一定意味着使用了向量寄存器,而不是表示循环在任何意义上都是并行的。相反,它的意思是原始循环迭代的M个实例被分组为单个迭代;在给定的新的“unwinded”/“未滚动”迭代中,旧的前迭代是依次执行的(所以您的猜测是绝对正确的)。
展开的目的通常是使循环更加“微体系结构/内存友好”。更多细节:通过使循环迭代更“胖”,通常可以改善CPU资源压力与内存/缓存资源压力之间的平衡,特别是在展开之后,您通常可以更有效地重用寄存器中的一些数据。
3) 展开+矢量化。编译器同时(用VL=N)和展开(通过M)对某些循环进行矢量化,这并不少见。因此,按照NxM的近似因子,优化循环中的迭代次数小于原始循环中的迭代次数,但是并行处理的元素数(在给定的时刻同时处理)仅为N。因此,在您的示例中,如果循环用VL=4向量化,并由2展开,则其伪代码可能如下所示:
DO IELEM = 1 , NELEM, 8
[X(IKLE(IELEM)),X(IKLE(IELEM+2)), X(IKLE(IELEM+4)), X(IKLE(IELEM+6))] = ...
[X(IKLE(IELEM+1)),X(IKLE(IELEM+3)), X(IKLE(IELEM+5)), X(IKLE(IELEM+7))] = ...
ENDDO
,其中方括号“对应”矢量寄存器内容。
4) 矢量化反对展开:
有第三个维度(我不太喜欢谈论它)。
当用户请求的向量长度大于给定硬件上的向量长度(假设为双FP为avx2平台指定向量长度(16))或混合不同类型时,则编译器可以(或不能)开始使用“虚拟向量寄存器”的概念并开始执行double-/quad-pumping.M-抽水是一种展开,但只适用于单指令(即抽水导致重复单指令,而展开则导致整个回路体重复)。你可以试着在最近的OpenMP书中读到关于m-抽吸的东西,比如given 一。因此,在某些情况下,您可能会以( a)向量化、( b)展开和( c)双泵浦的叠加结束,但这不是常见的情况,我将避免执行向量长度> 2*ISA_VectorLength。
https://stackoverflow.com/questions/32067818
复制