Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Optimization 是否可以通知编译器它';如果在环路外移动,安全吗?_Optimization_Compiler Construction_Preprocessor - Fatal编程技术网

Optimization 是否可以通知编译器它';如果在环路外移动,安全吗?

Optimization 是否可以通知编译器它';如果在环路外移动,安全吗?,optimization,compiler-construction,preprocessor,Optimization,Compiler Construction,Preprocessor,想象一下这样的Fortran代码 DO i=1,n IF (alpha == 0.0) THEN x(i) = y(i) ELSE x(i) = alpha*x(i)+y(i) END IF END DO (这当然只是一个愚蠢的例子,在实际情况下,循环将更加复杂,并且不容易作为数组构造重新写入) 由于alpha是一个常量,因此IF语句可以移到外部,并具有两个不同的循环,一个用于alpha==0,另一个用于其他情况。这应该更有效,因为IF只计算一次,但它会导致代码重复

想象一下这样的Fortran代码

DO i=1,n
  IF (alpha == 0.0) THEN
    x(i) = y(i)
  ELSE
    x(i) = alpha*x(i)+y(i)
  END IF
END DO
(这当然只是一个愚蠢的例子,在实际情况下,循环将更加复杂,并且不容易作为数组构造重新写入)

由于
alpha
是一个常量,因此
IF
语句可以移到外部,并具有两个不同的循环,一个用于
alpha==0
,另一个用于其他情况。这应该更有效,因为IF只计算一次,但它会导致代码重复,并且更难读取和维护


所以,我的问题是,编译器是否足够聪明,能够自行进行更改?如果
可以安全地移出循环,我能做些什么(例如使用预处理器指令)来通知编译器这个
。以下是GCC对以下等效C代码的作用:

void foo(int n, float x[], float y[], float alpha) 
{
  int i;
  for (i=0; i<n; i++) 
    {
      if (alpha == 0.0) x[i] = y[i];
      else x[i] = alpha*x[i]+y[i];
    }
}
带乘法的矢量化循环:

    movl    %edi, %r8d              ; %r8d = n
    xorps   %xmm1, %xmm1            ; clear xmm1
    shrl    $2, %r8d                ; %r8d = n >> 2
    xorl    %eax, %eax              ; clear eax = pointeur increment 
    xorl    %ecx, %ecx              ; clear ecx = (i>>2)
    leal    0(,%r8,4), %r9d         ; not relevant here
L19:
    movaps  %xmm1, %xmm0            ; xmm0 = 0
    addl    $1, %ecx                ; (i>>2) ++
    movlps  (%rdx,%rax), %xmm0      ; Load floats into xmm0 (vector registers)
    movhps  8(%rdx,%rax), %xmm0     ; Load floats into xmm0 (vector registers)
    movlps  %xmm0, (%rsi,%rax)      ; store floats in xmm0 into memory
    movhps  %xmm0, 8(%rsi,%rax)     ; store floats in xmm0 into memory
    addq    $16, %rax               ; increment pointer by 16
    cmpl    %r8d, %ecx              ; if (i>>2) < (n>>2)
    jb      .L19                    ; go back to .L19
                                    ; else finish the non vectorized part of the loop
    movaps  %xmm0, %xmm4            ; alpha -> xmm4
    movl    %edi, %r8d              ; %r8d = n
    shrl    $2, %r8d                ; %r8d = n >> 2
    xorps   %xmm3, %xmm3            ; clear xmm3
    shufps  $0, %xmm4, %xmm4        ; distribute xmm4 to all vector elements
    leal    0(,%r8,4), %r9d         ; not relevant here
    xorl    %eax, %eax              ; clear eax = pointeur increment 
    xorl    %ecx, %ecx              ; clear ecx = (i>>2)
.L11:
    movaps  %xmm3, %xmm1            ; xmm1 = 0
    addl    $1, %ecx                ; (i>>2) ++
    movaps  %xmm3, %xmm2            ; xmm2 = 0
    movlps  (%rsi,%rax), %xmm1      ; Load floats X into xmm1 (vector registers)
    movlps  (%rdx,%rax), %xmm2      ; Load floats Y into xmm2 (vector registers)
    movhps  8(%rsi,%rax), %xmm1     ; Load floats X into xmm1 (vector registers)
    movhps  8(%rdx,%rax), %xmm2     ; Load floats Y into xmm2 (vector registers)
    mulps   %xmm4, %xmm1            ; multiply xmm1 by xmm4
    addps   %xmm2, %xmm1            ; add xmm2 to xmm1
    movlps  %xmm1, (%rsi,%rax)      ; store floats in xmm1 into memory
    movhps  %xmm1, 8(%rsi,%rax)     ; store floats in xmm1 into memory
    addq    $16, %rax               ; increment pointer by 16
    cmpl    %r8d, %ecx              ; if i>>2 < n>>2 then
    jb      .L11                    ; go back to .L19
                                    ; else finish the non vectorized part of the loop
vs


最后一点意见:如果您想了解编译器在尝试改进循环时所做的工作,您应该了解一下

这种优化称为循环取消切换。它将示例的代码转换为

IF (alpha == 0.0) THEN
    DO i=1,n
        x(i) = y(i)
    END DO
ELSE
    DO i=1,n
       x(i) = alpha*x(i)+y(i)
    END DO
END IF

在支持此优化的编译器中,它可以通过编译器选项启用,例如In(
-loop unswitch
)和(查找
-funswitch loops
)。

由于您尚未指定语言,我将讨论C。说到优化,我发现C#编译器和抖动实际上都是毫无希望的!这是我测试的功能:

static void foo( int n, float[ ] x, float[ ] y, float alpha ) {
    System.Diagnostics.Debugger.Break( );

    for ( int i = 0; i < n; i++ ) {
        if ( alpha == 0.0 ) x[ i ] = y[ i ];
        else x[ i ] = alpha * x[ i ] + y[ i ];
    }

    Console.WriteLine( "END" );
}

因此,没有执行回路取消切换;还有其他一些非常基本的优化,比如使用寄存器保存函数参数。如果你问我,我觉得这很令人失望。

谢谢你的测试和评论。这看起来很有希望,但可能代码太简单,无法成为一个现实的测试:)@Jellby那么你应该想出一个现实的测试,因为这个答案非常好。顺便说一句,如果将某些内容移出循环对于提高性能非常重要,而您不信任自己的编译器,那么您应该将if语句移出循环。@Ali是的,当然,我的评论就是这个意思:我的示例可以改进。我将自己做一些进一步的测试,看看这个答案(正如你所说的,非常好)是否足够,或者我能想出更好的答案。顺便说一下,这两个循环都不是矢量化的:它们都是单浮点指令。是的,你是对的。谢谢我没有注意,因为使用了
xmm
寄存器。我没有复制代码的正确部分。我正在修理我的岗位。
IF (alpha == 0.0) THEN
    DO i=1,n
        x(i) = y(i)
    END DO
ELSE
    DO i=1,n
       x(i) = alpha*x(i)+y(i)
    END DO
END IF
static void foo( int n, float[ ] x, float[ ] y, float alpha ) {
    System.Diagnostics.Debugger.Break( );

    for ( int i = 0; i < n; i++ ) {
        if ( alpha == 0.0 ) x[ i ] = y[ i ];
        else x[ i ] = alpha * x[ i ] + y[ i ];
    }

    Console.WriteLine( "END" );
}
001F00C3    57              PUSH EDI
001F00C4    56              PUSH ESI
001F00C5    53              PUSH EBX
001F00C6    83EC 0C         SUB ESP,0C
001F00C9    8BF9            MOV EDI,ECX
001F00CB    8BF2            MOV ESI,EDX
001F00CD    8B5D 0C         MOV EBX,DWORD PTR SS:[EBP+0C]
001F00D0    D945 08         FLD DWORD PTR SS:[EBP+8]
001F00D3    D95D F0         FSTP DWORD PTR SS:[EBP-10]

                 System.Diagnostics.Debugger.Break( );
001F00D6    E8 9D7C915C     CALL 5CB07D78
001F00DB    D945 F0         FLD DWORD PTR SS:[EBP-10]
001F00DE    33D2            XOR EDX,EDX
001F00E0    85FF            TEST EDI,EDI
001F00E2    7F 04           JG SHORT 001F00E8
001F00E4    DDD8            FSTP ST
001F00E6    EB 45           JMP SHORT 001F012D

                 if ( alpha == 0.0 )
001F00E8    D9C0            FLD ST
001F00EA    DD5D E8         FSTP QWORD PTR SS:[EBP-18]
001F00ED    DD45 E8         FLD QWORD PTR SS:[EBP-18]
001F00F0    D9EE            FLDZ
001F00F2    DFF1            FCOMIP ST,ST(1)
001F00F4    DDD8            FSTP ST
001F00F6    7A 16           JPE SHORT 001F010E
001F00F8    75 14           JNE SHORT 001F010E

                 x[ i ] = y[ i ]
001F00FA    3B53 04         CMP EDX,DWORD PTR DS:[EBX+4]
001F00FD    73 4D           JNB SHORT 001F014C
001F00FF    D94493 08       FLD DWORD PTR DS:[EDX*4+EBX+8]
001F0103    3B56 04         CMP EDX,DWORD PTR DS:[ESI+4]
001F0106    73 44           JNB SHORT 001F014C
001F0108    D95C96 08       FSTP DWORD PTR DS:[EDX*4+ESI+8]
001F010C    EB 18           JMP SHORT 001F0126

                 else x[ i ] = alpha * x[ i ] + y[ i ];
001F010E    D9C0            FLD ST
001F0110    3B56 04         CMP EDX,DWORD PTR DS:[ESI+4]
001F0113    73 37           JNB SHORT 001F014C
001F0115    D84C96 08       FMUL DWORD PTR DS:[EDX*4+ESI+8]
001F0119    3B53 04         CMP EDX,DWORD PTR DS:[EBX+4]
001F011C    73 2E           JNB SHORT 001F014C
001F011E    D84493 08       FADD DWORD PTR DS:[EDX*4+EBX+8]
001F0122    D95C96 08       FSTP DWORD PTR DS:[EDX*4+ESI+8]

                 End of loop body
001F0126    42              INC EDX
001F0127    3BD7            CMP EDX,EDI
001F0129    7C BD           JL SHORT 001F00E8

                 Console.WriteLine( "END" );
001F012B    DDD8            FSTP ST
001F012D    E8 12F9305C     CALL 5C4FFA44
001F0132    8BC8            MOV ECX,EAX
001F0134    8B15 30201203   MOV EDX,DWORD PTR DS:[3122030]
001F013A    8B01            MOV EAX,DWORD PTR DS:[ECX]
001F013C    8B40 3C         MOV EAX,DWORD PTR DS:[EAX+3C]
001F013F    FF50 10         CALL DWORD PTR DS:[EAX+10]

                 return;
001F0142    8D65 F4         LEA ESP,[EBP-0C]
001F0145    5B              POP EBX
001F0146    5E              POP ESI
001F0147    5F              POP EDI
001F0148    5D              POP EBP
001F0149    C2 0800         RETN 8