分配时Fortran可分配数组的内存位置?

Posted

技术标签:

【中文标题】分配时Fortran可分配数组的内存位置?【英文标题】:Memory location of Fortran allocatable arrays on assigment? 【发布时间】:2019-12-27 14:13:02 【问题描述】:

假设我有类似的东西:

real, dimension(:), allocatable :: S
integer, dimension(:) :: idx
...
S = S(idx)

Sidx 在分配前已正确分配/初始化。

Fortran 标准对S 的内存位置(地址)有何规定(如果有的话)?分配后它应该留在同一个地方吗?是否未指定(由编译器决定)?如果S 不是allocatable 有区别吗?

完整示例:

$ cat test.f90 
program test
implicit none
real, dimension(:), allocatable :: S
integer :: i, idx(7) = [1,3,5,7,2,4,6]

allocate(S(size(idx)))
do i=1,size(S)
  S(i) = i*i
end do

write(6,*) S
write(6,*) loc(S)

S = S(idx)

write(6,*) S
write(6,*) loc(S)

S(:) = S(idx)

write(6,*) S
write(6,*) loc(S)

deallocate(S)

end program

$ sunf90 -V
f90: Studio 12.6 Fortran 95 8.8 Linux_i386 2017/05/30

$ sunf90 test.f90 ; ./a.out 
 1.0 4.0 9.0 16.0 25.0 36.0 49.0
 37518752
 1.0 9.0 25.0 49.0 4.0 16.0 36.0
 37519840
 1.0 25.0 4.0 36.0 9.0 49.0 16.0
 37519840

(假设loc给出了与数组地址相关的内容)

【问题讨论】:

【参考方案1】:

在您的示例中,idx 是否具有与S 相同的范围(元素数量)很重要。如果是这样,那么S(idx) 的形状与S 的形状相同,并且标准说不会发生S 的重新分配。但是如果它们不同,那么标准会说S 被释放,然后重新分配为S(idx) 的形状。如果发生这种重新分配/重新分配,如果基地址保持不变,则无法预测(并且可能不太可能)。

然后你问如果S 不可分配怎么办 - 在这种情况下,形状必须匹配并且它只是数据的副本,尽管可能通过临时数组,因为存在重叠。

-- 2019 年 8 月 24 日编辑--

我就此对 J3(美国 Fortran 标准委员会)电子邮件列表进行了调查。共识是,在没有TARGET 的情况下,“更改地址”是符合标准的,尽管不止一位成员质疑这是否是个好主意。编译器开发人员显然认为分配新存储并执行单个副本比保留相同的存储并执行两个副本(一个到临时,然后一个返回到S)更快。如果有很多的话,我可能会认为这是有益的。数据被复制了——也许——但不是在较小的情况下。

正如您所发现的,无论如何您都可以通过为S 赋予TARGET 属性来禁用此行为。

【讨论】:

即使没有de/re-allocation,我们能确定没有内存地址改变吗? (当然不会,但是否严格禁止?)可能是这些隐含要求之一,而不是明确要求? 谢谢!我实际上对形状匹配的情况很感兴趣,所以我知道它可以至少在没有重新分配或“填充”的情况下发生。无论如何,在这种情况下,Oracle (SunPro) 编译器正在重新分配(或至少更改地址)。 由于在相同形状的分配中没有重新分配,我看不出地址会如何变化。 (我应该提到,如果长度或种类类型不同,也会发生重新分配。)这肯定是我向 Oracle 抱怨的奇怪行为。你有多确定这正在发生?你能展示一个带有输出的例子吗?我会觉得很惊人。至于@francescalus 问题,该标准对“内在赋值的解释”有非常明确的字眼(10.2.1.3)。我会说不允许重新分配。 是的,Fortran 在形状/类型参数不变的情况下不允许重新分配,但如果 S 不是目标或需要在内存中修复, S 不能在内存中移动是否有标准原因?将S 的数组描述符简单地指向一个新位置(例如在非就地复制的情况下可能是临时数组)可能是一个有效的优化?就像我们不会在 Fortran 意义上称之为复制输入/复制输出机制重新分配一样。 (我问你是因为你是编译器专家,而不是因为我认为你错了。) 标准说分配是逐个元素发生的(没有定义的顺序)。你是对的,在实现方面,可以完成隐藏的重新分配和复制,我可以将性能方面视为“正常”方式,当两侧重叠时,涉及两个副本,但我不明白这是怎么回事可以被证明为符合行为。这是一个有趣的问题,我会向 J3 提出,看看他们怎么说。【参考方案2】:

Fortran 标准很少提及“内存位置”。但是,它确实有(Fortran 2018,注 16.24):

预计可分配对象的实现通常会涉及描述符来定位分配的存储

但是,在这个问题的情况下,可以合理地预期没有任何实现将始终保持S 在分配后的第一个元素具有相同的地址:分配后的S 可能任意大于@987654323 @在分配之前。在这种情况下,可能需要重新分配内存。

如果S 不可分配(严格来说,延迟长度),它的大小不会因为分配而改变:但是,它会(在许多但不是所有情况下)与基础标准兼容S 的地址移动到与临时数组相当的新位置。

在需要存储关联的情况下(这确实限制了变量在内存中的移动),可分配变量的使用受到严格限制。

【讨论】:

以上是关于分配时Fortran可分配数组的内存位置?的主要内容,如果未能解决你的问题,请参考以下文章

Fortran - 可分配派生类型的可分配数组

Fortran 可分配数组生命周期

使用 LAPACK 的 Fortran2003 中的动态内存分配错误

Fortran可分配数组和指针之间的等价性

如何将可分配数组传递给 Fortran 中的子例程

GDB 可以用于在 Fortran 90 中打印派生类型的可分配数组的值吗? [复制]