【问题标题】:Designing a derived type with array components使用数组组件设计派生类型
【发布时间】:2017-04-25 14:50:12
【问题描述】:

我一直在努力寻找有关设计派生类型的任何具体信息。我认为讨论这个问题的最佳方式是通过几个选项。我用派生类型的不同应用编写了一些代码部分。我更愿意为nparts、index 和refs 使用动态数组。我省略了实际使用该结构的代码部分(没有任何代码,因为我编造了它),但显示了一个示例,并且在例程中我打算至少使用该结构中的所有值一次。

选项 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 成为数组。请注意,在这个版本中,我们有一堆未使用的内存。例如,nparts 和 index 需要内存 packs-times,因为 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 是否值得?我认为分配的数组无论如何都不会按顺序显示。这是否表明选项 C 是最好的,因为 assembly 的每个部分都较小?
  3. 我应该将这个派生类型拆分为多个派生类型,还是完全摆脱它而只使用变量?我将在多个例程中使用此派生类型,并将其放入一个模块中。

【问题讨论】:

  • 您的编译器是否支持参数化派生类型?
  • 确实如此,虽然我不熟悉这个概念和应用。所以我的头脑还不支持它。 :)
  • kind=8 真的很难看,避免使用魔法常量!这并不意味着 8 个字节!至少不是在所有编译器中。

标签: arrays fortran structure dynamictype allocatable-array


【解决方案1】:
  1. 您希望变化最快的索引成为您最内层的循环。变化最快的索引是多维数组中的第一个。因此,选项 B 接近这个目标。虽然您可能想更改参考文献中尺寸的顺序。

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

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

【讨论】:

  • 1.哎呀,我的疏忽。我已经编辑了问题中的代码,所以这是正确的。 2.选项A中的SEQUENCE对优化有帮助吗?我的印象是,将这些信息放在内存中相邻的位置会提高循环内的计算速度。使用派生类型,其中每个循环的内存是连续的并且可分配的,会更好吗?例如,选项 C 的变体,其中index 不再是派生类型中的数组。
  • @Higgy 关于SEQUENCE,您指的是哪些信息?从性能的角度来看,将其放入派生数据类型通常是一个坏主意。正如我所说,它强制编译器使用你强加的顺序。因此,它的优化选项较少。你想要数组,这就是 Fortran 的亮点,我相信选项 B 为你提供了最好的变体。性能方面的选项 A 可能会更好,但您说您想使用动态内存分配。在我看来,C 的变化比 A 和 B 差。
  • 很好。这对我来说很清楚。 SEQUENCE 是否有任何好处?
  • @Higgy From fortran.com/fortran_storenew/Html/Info/books/key10.pdf: "使用 SEQUENCE 语句:(a) 允许这种类型的对象与存储相关联,或者 (b) 允许实际和虚拟参数具有相同的没有使用或主机关联的类型”。关于参数关联,不妨看看:software.intel.com/en-us/blogs/2009/03/31/…
  • 假设您使用的是 ifort,那么 REAL(KIND=8) 是可以的,表示 8 个字节,但是我在标签中看不到 intel-fortran。我可能会在编译器开关中使用数组对齐,或者在 ifort 中作为 !DIR$ ATTRIBUTES ALIGN : 64:: assembly.
猜你喜欢
  • 1970-01-01
  • 2010-10-20
  • 1970-01-01
  • 2014-10-18
  • 2016-03-27
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
相关资源
最近更新 更多