使用数组组件设计派生类型
Designing a derived type with array components
我一直在努力寻找有关设计派生类型的任何具体信息。我认为讨论这个问题的最佳方式是通过几个选项。我用派生类型的不同应用程序编写了一些代码段。我更愿意为 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
成为数组。请注意,在这个版本中,我们有一堆未使用的内存。例如,自 assembly(packs,packs,nboxes)
.
以来,nparts
和 index
需要内存 packs
次
! 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的另一种排列。
问题:
- 哪个版本是 correct/expected 为所示的
do
循环示例设计派生类型的方法?考虑到我想要动态数组功能,哪个版本最优化?
- 可能与上述有关。内存是如何分配和访问的?使用
SEQUENCE
是否值得?我认为分配的数组无论如何都不会按顺序显示。这难道不是因为 assembly
的每个部分都较小,所以选项 C 是最好的吗?
- 我是应该将这个派生类型拆分为多个派生类型,还是完全摆脱它而只使用变量?我将在多个例程中使用此派生类型并将其放入模块中。
您希望变化最快的索引成为最内层的循环。变化最快的索引是多维数组中的第一个。因此,选项 B 接近于这个目标。尽管您可能想更改 refs.
中维度的顺序
由索引 (i,j) 访问的形状为 (m,n) 的二维数组的内存布局按以下顺序给出:k = i+m*(j-1)
,其中k
表示内存中的一维索引。派生数据类型将包含对已分配内存的引用,所包含的可分配项的实际内存可能分散在内存中,但每个可分配数组本身都是连续的。因此,在您的选项 B 中, assembly
将是一个包含对可分配数组的引用的连续数组。 nparts
、index
和 refs
中的每一个本身都是连续的数组,但它们可以位于任意位置,在一个组装元素内或不同组装元素之间没有特定关系。使用 SEQUENCE
在这里没有任何意义,它强制编译器将派生数据类型的元素按您声明的顺序放入内存,并将禁止它重新排列数据类型组件它认为合适,这可能会限制性能。我怀疑,它会对你的例子产生很大的影响,但是当不需要它时你应该离开它。
不,选项 B 在我看来完全合理(除了 sequence
)。
我一直在努力寻找有关设计派生类型的任何具体信息。我认为讨论这个问题的最佳方式是通过几个选项。我用派生类型的不同应用程序编写了一些代码段。我更愿意为 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
成为数组。请注意,在这个版本中,我们有一堆未使用的内存。例如,自 assembly(packs,packs,nboxes)
.
nparts
和 index
需要内存 packs
次
! 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的另一种排列。
问题:
- 哪个版本是 correct/expected 为所示的
do
循环示例设计派生类型的方法?考虑到我想要动态数组功能,哪个版本最优化? - 可能与上述有关。内存是如何分配和访问的?使用
SEQUENCE
是否值得?我认为分配的数组无论如何都不会按顺序显示。这难道不是因为assembly
的每个部分都较小,所以选项 C 是最好的吗? - 我是应该将这个派生类型拆分为多个派生类型,还是完全摆脱它而只使用变量?我将在多个例程中使用此派生类型并将其放入模块中。
您希望变化最快的索引成为最内层的循环。变化最快的索引是多维数组中的第一个。因此,选项 B 接近于这个目标。尽管您可能想更改 refs.
中维度的顺序
由索引 (i,j) 访问的形状为 (m,n) 的二维数组的内存布局按以下顺序给出:
k = i+m*(j-1)
,其中k
表示内存中的一维索引。派生数据类型将包含对已分配内存的引用,所包含的可分配项的实际内存可能分散在内存中,但每个可分配数组本身都是连续的。因此,在您的选项 B 中,assembly
将是一个包含对可分配数组的引用的连续数组。nparts
、index
和refs
中的每一个本身都是连续的数组,但它们可以位于任意位置,在一个组装元素内或不同组装元素之间没有特定关系。使用SEQUENCE
在这里没有任何意义,它强制编译器将派生数据类型的元素按您声明的顺序放入内存,并将禁止它重新排列数据类型组件它认为合适,这可能会限制性能。我怀疑,它会对你的例子产生很大的影响,但是当不需要它时你应该离开它。不,选项 B 在我看来完全合理(除了
sequence
)。