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

Я изо всех сил пытался найти какую-либо конкретную информацию о разработке производного типа. Я думаю, что лучший способ обсудить это через несколько вариантов. Я составил несколько участков кода с различными приложениями производного типа. Я бы предпочел использовать динамические массивы для nparts, index и refs. Я пропустил разделы кода, которые на самом деле используют структуру (их нет, потому что я сам ее придумал), но показаны примеры, и в подпрограмме я намереваюсь использовать все значения в структуре хотя бы один раз.

Вариант А. Используйте статические массивы в производном типе. Недостатком является то, что мне пришлось бы угадывать размер массива во время компиляции.

! 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

Вариант Б. Использовать динамические массивы в производном типе.

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

person Higgy    schedule 10.12.2016    source источник
comment
Поддерживает ли ваш компилятор параметризованные производные типы?   -  person francescalus    schedule 10.12.2016
comment
Это так, хотя я не знаком с концепцией и применением. Так что мой разум еще не поддерживает это. :)   -  person Higgy    schedule 10.12.2016
comment
kind=8 действительно уродлив, избегайте магических констант! Это не значит 8 байт! По крайней мере, не во всех компиляторах.   -  person Vladimir F    schedule 10.12.2016


Ответы (1)


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

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

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

person haraldkl    schedule 10.12.2016
comment
1. Ой, оплошность с моей стороны. Я отредактировал код в вопросе, чтобы он был правильным. 2. Помогает ли SEQUENCE в варианте А с точки зрения оптимизации? У меня сложилось впечатление, что расположение этой информации рядом в памяти улучшит скорость вычислений в циклах. Было бы еще лучше использовать производный тип, в котором память непрерывна для каждого цикла и может быть распределена? Например, вариант варианта C, где index больше не является массивом в производном типе. - person Higgy; 10.12.2016
comment
@Higgy, какую информацию вы имеете в виду в отношении SEQUENCE? С точки зрения производительности в основном это плохая идея помещать его в производные типы данных. Как я уже сказал, это заставляет компилятор использовать заданный вами порядок. Таким образом, у него меньше возможностей для оптимизации. Вы хотите иметь массивы, вот где сияет Fortran, и я считаю, что вариант B дает вам лучший вариант. Вариант A с точки зрения производительности, вероятно, был бы даже лучше, но вы сказали, что хотели бы использовать динамическое выделение памяти. Варианты C хуже, чем A и B, на мой взгляд. - person haraldkl; 11.12.2016
comment
Очень хорошо. Это проясняет это для меня. Есть ли когда-нибудь причина, по которой SEQUENCE было бы полезно? - person Higgy; 11.12.2016
comment
@Higgy Из fortran.com/fortran_storenew/Html/Info/books/key10 .pdf: Оператор SEQUENCE используется: (a) для того, чтобы объекты этого типа могли быть связаны с хранилищем, или (b) чтобы разрешить фактическим и фиктивным аргументам иметь один и тот же тип без использования или связи с хостом. Об ассоциации аргументов, возможно, взгляните на: software.intel.com/en-us/blogs/2009/03/31/ - person haraldkl; 11.12.2016
comment
Предполагая, что вы используете ifort, тогда REAL (KIND = 8) в порядке и означает 8 байтов, однако я не вижу intel-fortran в тегах. Вероятно, я бы использовал выравнивание массива в переключателях компилятора или в ifort как сборку !DIR$ ATTRIBUTES ALIGN : 64:: . - person Holmz; 12.12.2016