Использование одного фиктивного аргумента для массивов различной формы - PullRequest
0 голосов
/ 07 июня 2018

Допустим, у нас есть несколько различных смежных массивов

real :: a1(2),a2(2),a3(2)
real :: b1(2,2), b2(2,2),b3(2,2)
real :: c1(3,3,3), c2(3,3,3),c3(3,3,3)

и подпрограмма

subroutine mtpy(x,y,z)
  real,contiguous, intent(in) :: x(:), y(:)
  real, intent(out) :: z(size(x))

  z=x*y
end subroutine mtpy

Как использовать mtpy в следующих сериях вызовов:

call mtpy(a1,a2,a3)
call mtpy(b1,b2,b3)
call mtpy(c1,c2,c3)

Очевидно, это приведет к ошибкам компилятора, поскольку форма фактических и фиктивных аргументов не совпадает.В подобных случаях я обычно объявлял несколько конкретных процедур, каждая из которых обрабатывает определенную форму, а затем оборачивал их все с помощью интерфейса.Однако это довольно утомительно (представьте, что у вас огромное количество простых элементарных функций и чистых процедур, которые обрабатывают многомерные массивы (до трех измерений) как одномерные массивы, а затем предоставляют экземпляры, такие как sub_1d, sub_2d, sub_3d, .. для каждого изони, несмотря на то, что все они фактически выполняют одну и ту же работу).

Одним из частичных решений является, я полагаю, использование RESHAPE

call mtpy(reshape(b1,[4]),reshape(b2,[4]),bb)

, но могу ли я быть уверен, что компилятор (в основном меня интересует gfortran и ifort) не начнет создавать1d временных, чтобы держать измененные массивы b1 и b2?

Теперь я также знаю, что можно объявить указатель массива, например

real, pointer, contiguous :: p1(:),p2(:),p3(:)

, и сделать следующее присвоение указателя, например

p1(1:size(c1))=>c1

Однако этоНедостаток подхода заключается в том, что мне нужно объявить исходные массивы в качестве целей.Разве это не повлияет на оптимизацию, которую сможет выполнить компилятор?

Еще одно решение, я полагаю, заключается в использовании массивов предполагаемого размера, но я заметил, что Metcalf и др. Называют их использование«устарел» и, опять же, я не уверен насчет влияния на omptimisations.

Итак, есть ли простой способ обработки многомерного массива Фортрана как одного массива измерений (в подпрограмме или функции) что не накладывает ненужных предположений (таких как TARGET) на этот массив?Если бы я мог использовать RESHAPE, не опасаясь создания временных (я имею дело только с непрерывными массивами), я бы пошел на это.Есть предложения?

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

Будущий стандарт Fortran 2018 предоставит массивы предполагаемого ранга (которые позволят получать массивы любого ранга) и конструкцию select rank, которая позволит легко решать подобные ситуации с одним массивом предполагаемого ранга (см., Например,, Новые функции Fortran 2018 , начиная со страницы 16), и с большим количеством трудностей с несколькими массивами предполагаемого ранга.

Массивы предполагаемого размера, хотя и не модные или не рекомендуемые, действительны, не-подростковые свойства текущего стандарта (Fortran 2008), а также следующего стандарта проекта (Fortran 2018), так что они могут быть использованы при необходимости.Поскольку от этого зависит большая часть кода на Fortran 77, а многим из него уже десятки лет, я ожидаю, что он будет значительно оптимизирован в большинстве компиляторов.

Однако вам не нужно использовать массивы предполагаемого размераВы можете использовать массивы явной формы (массивы с явными измерениями), и, пока фактические аргументы массива имеют достаточно элементов, код будет действительным, поскольку, согласно пункту 4 раздела 12.5.2.11 стандарта 2008 года,

Фактический аргумент, который представляет последовательность элементов и соответствует фиктивному аргументу, который является массивом, является последовательностью, связанной с фиктивным аргументом, если фиктивный аргумент является массивом явной формы или предполагаемого размера.Ранг и форма фактического аргумента не обязательно должны совпадать с рангом и формой фиктивного аргумента, но количество элементов в фиктивном аргументе не должно превышать количество элементов в последовательности элементов фактического аргумента.Если фиктивный аргумент имеет предполагаемый размер, количество элементов в фиктивном аргументе равно количеству элементов в последовательности элементов.

Таким образом, вы можете

call mtpy(a1,a2,a3,size(a3))
call mtpy(b1,b2,b3,size(b3))
call mtpy(c1,c2,c3,size(c3))
...
subroutine mtpy(x,y,z,n)
  integer, intent(in) :: n
  real, intent(in) :: x(n), y(n)
  real, intent(out) :: z(n)
  z=x*y
end subroutine mtpy
0 голосов
/ 07 июня 2018

Поскольку я также не уверен, создает ли reshape() временный массив даже для смежных случаев, я попытался распечатать адрес оригинала и переданных массивов с помощью c_loc().Тогда даже для небольших одномерных массивов reshape() в gfortran-8 и ifort-16, кажется, создает временные эффекты (потому что адрес первого элемента отличается).Таким образом, кажется более безопасным предположить, что временные фильтры создаются даже для простых случаев (для получения дополнительной информации, пожалуйста, смотрите комментарии francescalus ниже.)

module test
    use iso_c_binding, only: c_loc
    implicit none

    interface linear
        module procedure linear_r2d, linear_r3d
    endinterface
contains

subroutine calc_ver1( a )  !! assumed-shape dummy array
    real, contiguous, target :: a(:)
    print *, "addr = ", c_loc( a(1) )
    print *, "vals = ", a
endsubroutine

subroutine calc_ver2( a, n )  !! explicit-shape dummy array
    integer      :: n
    real, target :: a( n )
    print *, "addr = ", c_loc( a(1) )
    print *, "vals = ", a
endsubroutine

function linear_r2d( a ) result( ptr )  !! returns a 1-d pointer from 2-d array
    real, contiguous, target  :: a(:,:)
    real, contiguous, pointer :: ptr(:)
    ptr( 1 : size(a) ) => a
endfunction

function linear_r3d( a ) result( ptr )  !! returns a 1-d pointer from 3-d array
    real, contiguous, target  :: a(:,:,:)
    real, contiguous, pointer :: ptr(:)
    ptr( 1 : size(a) ) => a
endfunction

endmodule

program main
    use test
    implicit none
    integer i
    real, target :: a(2), b(2,2), c(2,2,2)

    a = [1,2]
    b = reshape( [( 2*i, i=1,4 )], [2,2]   )
    c = reshape( [( 3*i, i=1,8 )], [2,2,2] )

    print *, "addr(a) = ", c_loc( a(1) )
    print *, "addr(b) = ", c_loc( b(1,1) )
    print *, "addr(c) = ", c_loc( c(1,1,1) )

    print *, "[ use assumed-shape dummy ]"
    call calc_ver1( a )
    ! call calc_ver1( b )  ! rank mismatch
    ! call calc_ver1( c )  ! rank mismatch

    print *, "--- with reshape() ---"
    call calc_ver1( reshape( b, [size(b)] ) )
    call calc_ver1( reshape( c, [size(c)] ) )

    print *, "--- with linear() ---"
    call calc_ver1( linear( b ) )
    call calc_ver1( linear( c ) )

    print *
    print *, "[ use explicit-shape dummy ]"
    call calc_ver2( a, size(a) )
    call calc_ver2( b, size(b) )
    call calc_ver2( c, size(c) )
end

Результат ifort-16 в Linux:

 addr(a) =                7040528
 addr(b) =                7040544
 addr(c) =                7040560
 [ use assumed-shape dummy ]
 addr =                7040528
 vals =    1.000000       2.000000    
 --- with reshape() ---
 addr =        140736361693536
 vals =    2.000000       4.000000       6.000000       8.000000    
 addr =        140736361693560
 vals =    3.000000       6.000000       9.000000       12.00000       15.00000       18.00000       21.00000       24.00000  
 --- with linear() ---
 addr =                7040544
 vals =    2.000000       4.000000       6.000000       8.000000    
 addr =                7040560
 vals =    3.000000       6.000000       9.000000       12.00000       15.00000       18.00000       21.00000       24.00000    

 [ use explicit-shape dummy ]
 addr =                7040528
 vals =    1.000000       2.000000    
 addr =                7040544
 vals =    2.000000       4.000000       6.000000       8.000000    
 addr =                7040560
 vals =    3.000000       6.000000       9.000000       12.00000       15.00000       18.00000       21.00000       24.00000    

Результат gfortran-8 для OSX10.11:

 addr(a) =       140734555734776
 addr(b) =       140734555734752
 addr(c) =       140734555734720
 [ use assumed-shape dummy ]
 addr =       140734555734776
 vals =    1.00000000       2.00000000    
 --- with reshape() ---
 addr =       140734555734672
 vals =    2.00000000       4.00000000       6.00000000       8.00000000    
 addr =       140734555733984
 vals =    3.00000000       6.00000000       9.00000000       12.0000000       15.0000000       18.0000000       21.0000000       24.0000000    
 --- with linear() ---
 addr =       140734555734752
 vals =    2.00000000       4.00000000       6.00000000       8.00000000    
 addr =       140734555734720
 vals =    3.00000000       6.00000000       9.00000000       12.0000000       15.0000000       18.0000000       21.0000000       24.0000000    

 [ use explicit-shape dummy ]
 addr =       140734555734776
 vals =    1.00000000       2.00000000    
 addr =       140734555734752
 vals =    2.00000000       4.00000000       6.00000000       8.00000000    
 addr =       140734555734720
 vals =    3.00000000       6.00000000       9.00000000       12.0000000       15.0000000       18.0000000       21.0000000       24.0000000

И я также думаю, что фиктивные массивы явной формы полезны в зависимости от случаев, и код в Вопросе кажется именно таким.(Поскольку фактический аргумент является смежным, массив не создается временно.) Если аргумент размера n в calc_ver2() нежелателен, мы могли бы использовать функцию, которая возвращает указатель на 1-й массив (см. linear() выше).), но я думаю, что это может быть излишним, учитывая простоту calc_ver2() ... (Кстати, я прикрепил target в различных местах кода, что просто потому, что c_loc() требует этого).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...