Разработка производного типа с компонентами массива
Я изо всех сил пытался найти конкретную информацию, касающуюся разработки производного типа. Я думаю, что лучший способ обсудить это через пару вариантов. Я составил несколько разделов кода с различными приложениями производного типа. Я бы предпочел использовать динамические массивы для 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
раз 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.
Вопросы:
- Какая версия является правильным / ожидаемым методом проектирования производного типа для
do
пример цикла показан? Какая версия наиболее оптимизирована, учитывая, что мне нужны возможности динамического массива? - Может быть связано с выше. Как распределяется и осуществляется доступ к памяти? Является ли использование
SEQUENCE
даже стоит? Я думаю, что выделенные массивы не будут отображаться последовательно в любом случае. Разве это не указывает на то, что вариант C является лучшим, так как каждый разделassembly
меньше? - Должен ли я разделить этот производный тип на несколько производных типов или избавиться от него вообще и просто придерживаться переменных? Я буду использовать этот производный тип для нескольких процедур и поместил бы его в модуль.
1 ответ
Вы хотите, чтобы самый быстро меняющийся индекс был вашим внутренним циклом. Самый быстро меняющийся индекс - первый в многомерном массиве. Таким образом, вариант B приближается к этой цели. Хотя вы можете изменить порядок измерений в ссылках.
Макет памяти для двумерного массива формы (m,n), к которому обращаются индексы (i,j), определяется следующим порядком:
k = i+m*(j-1)
, гдеk
обозначает одномерный индекс в памяти. Производный тип данных будет содержать ссылку на выделенную память, а фактическая память содержащихся в ней выделяемых может быть разбросана по памяти, но каждый выделяемый массив является смежным сам по себе. Итак, в вашем варианте Bassembly
будет непрерывным массивом, содержащим ссылки на размещаемые массивы. Каждый изnparts
,index
а такжеrefs
сами по себе могут быть смежными массивами, но могут быть расположены в произвольных местах без какого-либо конкретного отношения внутри одного элемента сборки или между различными элементами сборки. ИспользованиеSEQUENCE
здесь не имеет никакого смысла, это вынуждает компилятор помещать элементы производного типа данных в память в указанном вами порядке и запрещает переупорядочивать компоненты типа данных по своему усмотрению, что может ограничить производительность. Я сомневаюсь, что в вашем примере это будет иметь серьезные последствия, но когда это не нужно, вы должны оставить это.Нет, вариант B выглядит вполне разумным, на мой взгляд (за исключением
sequence
).