Разработка производного типа с компонентами массива

Я изо всех сил пытался найти конкретную информацию, касающуюся разработки производного типа. Я думаю, что лучший способ обсудить это через пару вариантов. Я составил несколько разделов кода с различными приложениями производного типа. Я бы предпочел использовать динамические массивы для 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.

Вопросы:

  1. Какая версия является правильным / ожидаемым методом проектирования производного типа для do пример цикла показан? Какая версия наиболее оптимизирована, учитывая, что мне нужны возможности динамического массива?
  2. Может быть связано с выше. Как распределяется и осуществляется доступ к памяти? Является ли использование SEQUENCE даже стоит? Я думаю, что выделенные массивы не будут отображаться последовательно в любом случае. Разве это не указывает на то, что вариант C является лучшим, так как каждый раздел assembly меньше?
  3. Должен ли я разделить этот производный тип на несколько производных типов или избавиться от него вообще и просто придерживаться переменных? Я буду использовать этот производный тип для нескольких процедур и поместил бы его в модуль.

1 ответ

Решение
  1. Вы хотите, чтобы самый быстро меняющийся индекс был вашим внутренним циклом. Самый быстро меняющийся индекс - первый в многомерном массиве. Таким образом, вариант B приближается к этой цели. Хотя вы можете изменить порядок измерений в ссылках.

  2. Макет памяти для двумерного массива формы (m,n), к которому обращаются индексы (i,j), определяется следующим порядком: k = i+m*(j-1), где k обозначает одномерный индекс в памяти. Производный тип данных будет содержать ссылку на выделенную память, а фактическая память содержащихся в ней выделяемых может быть разбросана по памяти, но каждый выделяемый массив является смежным сам по себе. Итак, в вашем варианте B assembly будет непрерывным массивом, содержащим ссылки на размещаемые массивы. Каждый из nparts, index а также refs сами по себе могут быть смежными массивами, но могут быть расположены в произвольных местах без какого-либо конкретного отношения внутри одного элемента сборки или между различными элементами сборки. Использование SEQUENCE здесь не имеет никакого смысла, это вынуждает компилятор помещать элементы производного типа данных в память в указанном вами порядке и запрещает переупорядочивать компоненты типа данных по своему усмотрению, что может ограничить производительность. Я сомневаюсь, что в вашем примере это будет иметь серьезные последствия, но когда это не нужно, вы должны оставить это.

  3. Нет, вариант B выглядит вполне разумным, на мой взгляд (за исключением sequence).

Другие вопросы по тегам