使用数组组件

时间:2016-12-10 04:03:16

标签: arrays fortran structure dynamictype allocatable-array

我一直在努力寻找设计衍生类型的具体信息。我认为讨论这个的最好方法是通过几个选项。我已经用派生类型的不同应用程序编写了一些代码段。我更喜欢为npartsindexrefs使用动态数组。我省略了实际使用该结构的代码部分(由于我已经完成了,因此没有任何代码),但是显示了一个示例,并且在例程中我打算至少使用一次结构中的所有值。

选项A:在派生类型中使用静态数组。缺点是我必须在编译时猜测数组大小。

! Known before compile time.
nboxes = 5000
max_parts = 2000
packs = 10

Type Boxes
   Sequence
   Integer :: location, date
   Integer, Dimension(0:packs) :: nparts
   Integer, Dimension(max_parts,packs) :: index
   Real(Kind=8), Dimension(packs,packs) :: refs
End Type Boxes

type(boxes), dimension(:), allocatable :: assembly
allocate(assembly(nboxes))

! Perform some operations on assembly...
do i = 1,nboxes
   do j = 1,packs
      do k = j,packs
         example = assembly(i)%nparts(k) - assembly(i)%nparts(j)
         .
         .
         do m = 1,max_parts
            example = assembly(i)%index(m,j) + assembly(i)%refs(k,j) * assembly(i)%nparts(j)
            .
            .
         end do
      end do
   end do
end do

选项B:在派生类型中使用动态数组。

! Defined during execution. Much better.
nboxes = 5000
max_parts = 2000
packs = 10

Type Boxes
   Sequence
   Integer :: location, date
   Integer, Dimension(:), Allocatable :: nparts
   Integer, Dimension(:,:), Allocatable :: index
   Real(Kind=8), Dimension(:,:), Allocatable :: refs
End Type Boxes

type(boxes), dimension(:), allocatable :: assembly
allocate(assembly(nboxes))
do i = 1,nboxes
   allocate(assembly(i)%nparts(0:packs))
   allocate(assembly(i)%index(max_parts,packs))
   allocate(assembly(i)%refs(packs,packs))
end do

! Perform some operations on assembly...
do i = 1,nboxes
   do j = 1,packs
      do k = j,packs
         example = assembly(i)%nparts(k) - assembly(i)%nparts(j)
         .
         .
         do m = 1,max_parts
            example = assembly(i)%index(m,j) + assembly(i)%refs(k,j) * assembly(i)%nparts(j)
            .
            .
         end do
      end do
   end do
end do

选项C:最大限度地减少派生类型中使用的动态数组的数量,并强制assembly成为数组。请注意,在这个版本中,我们有一堆未使用的内存。例如,npartsindex需要记忆packs - 自assembly(packs,packs,nboxes)以来的时间。

! Defined during execution. Much better.
nboxes = 5000
max_parts = 2000
packs = 10

Type Boxes
   Sequence
   Integer :: location, date, nparts, index
   Real(Kind=8) :: refs
   Integer, Dimension(:), Allocatable :: index
End Type Boxes

type(boxes), dimension(:,:,:), allocatable :: assembly
allocate(assembly(packs,packs,nboxes))
do i = 1,nboxes
   do j = 1,packs
      do k = 1,packs
         allocate(assembly(k,j,i)%index(max_parts))
      end do
   end do
end do

! Perform some operations on assembly...
do i = 1,nboxes
   do j = 1,packs
      do k = j,packs
         example = assembly(k,j,i)%nparts - assembly(k,j,i)%nparts
         .
         do m = 1,max_parts
            example = assembly(k,j,i)%index(m) + assembly(k,j,i)%refs * assembly(k,j,i)%nparts
            .
            .
         end do
      end do
   end do
end do

选项D:选项C的另一种排列。

问题:

  1. 哪个版本是为显示的do循环示例设计派生类型的正确/预期方法?考虑到我想要动态阵列功能,哪个版本最优化?
  2. 可能与上述有关。如何分配和访问内存? SEQUENCE的使用是否值得?我认为分配的数组无论如何都不会按顺序出现。由于assembly的每个部分都较小,因此选项C是最好的吗?
  3. 我是否可以将此派生类型拆分为多个派生类型或完全摆脱它并坚持变量?我将在多个例程中使用此派生类型,并将其放在模块中。

1 个答案:

答案 0 :(得分:2)

  1. 您希望最快变化的索引成为您最内层的循环。变化最快的索引是多维数组中的第一个。因此,选项B接近这个目标。虽然您可能想要更改参考中维度的顺序。

  2. 由索引(i,j)访问的二维形状数组(m,n)的内存布局由以下顺序给出:k = i+m*(j-1),其中{{ 1}}表示内存中的一维索引。派生的数据类型将包含对分配的内存的引用,并且包含的​​可分配的实际内存可能分散在内存中,但每个可分配的数组本身是连续的。因此,在您的选项B中,k将是一个包含对可分配数组的引用的连续数组。 assemblynpartsindex中的每一个本身都是连续的数组,但可以位于任意位置,在一个装配元素内或不同的装配元素之间没有特定的关系。使用refs在这里没有任何意义,它强制编译器按顺序将派生数据类型的元素放入内存中,并声明并禁止它重新排列数据类型组件因为它认为合适,这可能会限制性能。我怀疑,它会在你的例子中产生很大的影响,但是当你不需要它时你应该离开它。

  3. 不,在我看来,选项B看起来非常合理(SEQUENCE除外)。