Fortran 90自动(?)分配可分配项
在上面的代码中,我设置了两个可分配项-Fortran 90自动(?)分配可分配项,fortran,allocation,fortran90,Fortran,Allocation,Fortran90,在上面的代码中,我设置了两个可分配项-a和b,并且只在代码中分配了a。我原以为代码无法编译,但它编译得很好,而且似乎工作得很好,而下面的代码也显示了SEGFAULT,它做了类似的工作 program test implicit none real(dp),allocatable :: a(:), b(:) allocate (a(10)) a = 1.d0 (*) b = a end program 理解前一个代码自动分配b是否合适 program te
a
和b
,并且只在代码中分配了a
。我原以为代码无法编译,但它编译得很好,而且似乎工作得很好,而下面的代码也显示了SEGFAULT,它做了类似的工作
program test
implicit none
real(dp),allocatable :: a(:), b(:)
allocate (a(10))
a = 1.d0
(*) b = a
end program
理解前一个代码自动分配b
是否合适
program test_fail
implicit none
real(dp),allocatable :: a(:), b(:)
integer :: i
allocate ( a(10) )
a = 1.d0
do i = 1, 10
b(i) = a(i)
enddo
end program
同样,在上述带有成形数组输入的子例程中,代码自动检测输入数组a
的大小,并在子例程中分配自己的数组,并将其释放回其上一帧,这样理解是否正确
最后,将一个数组复制到另一个数组时,哪个更快
subroutine test_sub(a)
implicit none
real(dp),intent(inout) :: a(:,:)
...
end program
我没有太多的时间,所以这可能是相当压缩。还要注意的是,正如上面的一条评论所指出的(为了突出和子孙后代,复制到这里),代码和这个答案的有效性取决于版本(更多细节请参见) 正如你所发现的,声明
program speed2
implicit none
real(dp), allocatable :: a(:,:,:), b(:,:,:)
integer :: i, j, k
allocate( a(10000,10000,10000), b(10000,10000,10000) )
a = 1.d0
do i = 1,10000
do j = 1,10000
do k = 1,10000
b(i,j,k) = a(i,j,k)
enddo
enddo
enddo
end program
b = a
b(i) = a(i)
将自动将数组b
分配到与a
相同的大小(和形状),并为其元素提供与a
元素相同的值。这一切都符合标准。然而,声明
program speed2
implicit none
real(dp), allocatable :: a(:,:,:), b(:,:,:)
integer :: i, j, k
allocate( a(10000,10000,10000), b(10000,10000,10000) )
a = 1.d0
do i = 1,10000
do j = 1,10000
do k = 1,10000
b(i,j,k) = a(i,j,k)
enddo
enddo
enddo
end program
b = a
b(i) = a(i)
左侧没有数组
,它有数组元素
,在这种情况下Fortran不会自动分配数组。这是不幸的,但(我相信)这是语言标准不要求编译器发现错误的情况之一——这就是为什么您在运行时会发现错误。您的编译器的行为似乎与我最近使用过的其他编译器一样—实际上,没有元素b(1)
来分配a(1)
的值,等等
至于语句中的子例程中的“分配”
program speed2
implicit none
real(dp), allocatable :: a(:,:,:), b(:,:,:)
integer :: i, j, k
allocate( a(10000,10000,10000), b(10000,10000,10000) )
a = 1.d0
do i = 1,10000
do j = 1,10000
do k = 1,10000
b(i,j,k) = a(i,j,k)
enddo
enddo
enddo
end program
b = a
b(i) = a(i)
那不太一样。这里,a
是假定的形状,只是假定传递给例程的相应参数的形状(和值)。《标准》没有说明这是如何做到的。大多数编译器不会复制数组(出于对Fortran程序员通常很重要的性能原因),但有些编译器可能会这样做,并且很容易构造示例,其中大多数编译器将复制作为参数传递的数据结构
至于最后两个codelet中哪一个更快?为什么不告诉我们?High Performance Mark的答案是正确的,而且很有帮助,但是以稍微不同的方式重复一些要点可能会有一些好处 如前所述,在该答案和相关问题中,作业如下
real(dp),intent(inout) :: a(:,:)
对于b
未分配取决于是否使用Fortran 2003+规则。但是,如果未分配b
,则分配给b
的元素,如中所示
b = a
总是错误的(直到当前的语言版本)。每当数组元素出现在赋值的左侧(或变量引用中)时,下标的值必须在数组的边界内-但未分配的可分配数组没有定义的边界。1
这与Matlab等语言形成了对比,在Matlab中,如果存在超出当前数组大小范围的赋值,则数组会“增长”。Fortran(2003+)整个数组分配是一个例外,其中(重新)分配发生在整个数组完全位于右侧的情况下。在这样的整个数组分配上,所需的大小是已知的;通过逐元素分配,最终分配最初是未知的
关于子程序test\u sub
的伪参数a
,High Performance Mark的回答中有一点需要补充:这意味着子程序中的a
可能与调用子程序的主程序中的数组共享某些方面。这确实可能包括之前给出的任何分配/存储
不过,为了完整起见:Fortran是那些语义不是逐行定义的语言之一。子程序的…
隐藏了其他人可能感兴趣的内容。在这里面,…
我们可以改变a
的这种解释
b(i) = a(i)
突然间,a
不是假定的形状,而是延迟的形状。我不会在这里讨论后果,但它们可以在其他问题和答案中找到
最后,在赋值速度方面,我们比较了整个数组赋值和数组所有元素上的循环(左侧正确分配)。Fortran的规则保证,对于左侧的数组,赋值是“在相应的数组元素上逐元素执行”,但“处理器可以按任何顺序逐元素执行赋值”
您的赋值(在大多数情况下)具有相同的效果,但编译器可能会根据需要重新排序或利用矢量化/并行化。然而,同样地,它可以对循环进行完全相同的分析(包括重新排序循环,正如伊恩·布什的评论所指出的那样,这可能会有所帮助,或者是为了响应OpenMP指令)。如果您想知道案例中性能的精确细节,您必须进行测试,但如果您使用手工制作的循环进行微优化,最终会惹恼某人,就像您使用整个数组分配惹恼其他人一样
1对于感兴趣的一方,请参见Fortran 2018 8.5.8.4和9.5.3.1。让我扩展我的评论作为回答 1st代码截断编译是因为
b
在赋值b=a
时采用a
的形状,这是Fortran的标准功能
2nd代码段错误:未分配的数组无法索引,它没有形状或条目。这是一个错误
(在这个简单的例子中,编译器可以很容易地检测到这个错误。)