TARGET 属性(不是 POINTER 属性)的影响性能

Impact performance of the TARGET attribute (not the POINTER attribute)

TARGET 属性的存在对性能的影响问题被问了很多次,但答案缺乏具体的例子。

问题 Why does a Fortran POINTER require a TARGET? 有一个很好的答案:

An item that might be pointed to could be aliased to another item, and the compiler must allow for this. Items without the target attribute should not be aliased and the compiler can make assumptions based on this and therefore produce more efficient code.

我知道优化取决于编译器和处理器指令集,但这些优化实际上是什么?

考虑以下代码:

subroutine complexSubroutine(tab)
    double precision, dimension(:) :: tab
    !...
    ! very mysterious complex instructions performed on tab
    !...
end subroutine

编译器可以对此代码执行哪些优化

double precision, dimension(very large number) :: A
call complexSubroutine(A)

而不是这个代码?

double precision, dimension(very large number), target :: A
call complexSubroutine(A)

请注意,问题 complexSubroutine 中的伪参数 tab 没有 TARGET 属性。 在子例程 中,编译器可以假设 tab 没有别名,下面的讨论没有实际意义。 该问题适用于调用子例程的范围。

如果编译器知道不存在别名的可能性,那么编译器就知道两个对象的存储不存在重叠的可能性。重叠的可能性对某些操作有直接影响。作为一个简单的例子,请考虑:

INTEGER, xxx :: forward(3)
INTEGER, xxx :: reverse(3)
forward = [1,2,3]
reverse(1:3:1) = forward(3:1:-1)

Fortran 标准定义了赋值的语义,因此 reverse 应该以值 [3,2,1] 结束。

如果 xxx 不包含 POINTER 或 TARGET,那么编译器知道 forwardreverse 不重叠。它可以以直接的方式执行数组赋值,也许通过下标三元组建议的方式向后处理右侧的元素并向前处理左侧的元素,然后逐个元素赋值 "directly".

但是,如果 forwardreverse 是 TARGET,那么它们很可能会重叠。上面建议的直接方式可能无法产生标准要求的结果。如果这两个名称与完全相同的底层数据对象序列相关联,那么 reverse(3)forward(1) 的传输将更改 reverse(1) 稍后引用的值。使用上面无法适应别名的天真方法,reverse 最终会得到值 [3,2,3].

为了提供标准要求的结果,编译器可能会创建一个临时对象来保存赋值右侧的评估结果,有效地:

INTEGER, TARGET :: forward(3)
INTEGER, TARGET :: reverse(3)
INTEGER :: temp(3)
temp = forward(3:1:-1)
reverse(1:3:1) = temp

与临时文件相关的存在和其他操作可能会影响性能。

别名可能会破坏原本简单直接的操作方法,这是许多情况下的普遍问题。在没有编译器和运行时智能来确定别名在特定情况下不是问题的情况下,创建和使用临时文件是一种通用解决方案,具有对性能影响的一般潜力。

编译器不会立即发现别名的可能性也可能会阻止编译器假设对象的值在没有看到任何暗示更改的对象的显式引用时不会更改。

INTEGER, TARGET :: x
...much later...
x = 4
CALL abc
IF (x == 4) THEN
  ...

编译器对过程 abc 一无所知,因此通常不能假设 x 在过程 abc 中没有被修改 - 可能是指向 [=29= 的指针] 以某种方式可用于该过程,并且该过程已使用该指针间接修改 x。如果 x 没有 TARGET 属性,那么编译器就知道 abc 不能合法地改变它的值。这对编译器在编译时分析可能​​的代码路径以及在循环外重新排序 operations/move 操作等的能力有影响。