【问题标题】:Fortran tips in large modules大型模块中的 Fortran 技巧
【发布时间】:2014-10-10 04:08:31
【问题描述】:

我有一个由许多小子例程和一个主子例程组成的模块,这是唯一一个公开的。其余子例程是私有的,由主子例程或在其中调用。主子例程必须接受所有必要的参数来执行它的工作,但通常当它向私有子例程交付任务时,它必须再次传递一些参数。我想在处理数组时避免这种情况。使用标量数字,我可以简单地定义一个模块范围的变量并在主子程序中为其分配相应的值:

module test
    integer, private :: m, n
    private :: foo
    public :: main

contains

    subroutine main(matrixA, m0, n0)
        integer, intent(in) :: m0, n0
        real, intent(inout) :: matrixA(m0,n0)

        !assign values to module variables m & n
        m = m0
        n = n0
        ...

        !no need to pass m0 & n0
        call foo(matrixA)
    end subroutine

    subroutine foo(matrixA)
        real, intent(inout) :: matrixA(m,n)

        ...

    end subroutine
end module

我也想根本不需要传递matrixA。做这个的最好方式是什么?最好,我的意思是提供最好的性能。

【问题讨论】:

  • 您是否分析了代码以声称参数传递是瓶颈?如果您发现在 looos 中假设的形状数组太慢,请尝试使用 contiguous 属性。
  • 是的,它肯定更慢,比如显式形状需要 30 秒,而假设形状需要 40 秒多一点。此外,我尝试使用模块范围的指针,因此在 main 中我只需将这些指针分配给矩阵,并让其余的子例程使用这些指针。它甚至更慢(48 秒)。我将尝试contiguous,它在 Fortran 90 中有效吗?这个link 有更多关于假设形状性能的信息。
  • contiguous 是 Fortran 2008。Fortran 90 基本上已经死了,不要关心早于 95 的任何东西。
  • 您能否展示对main 的调用和声明的主要程序部分?

标签: module fortran subroutine


【解决方案1】:

我无法评论“最佳”方式,但有一些话要说。现在问题已被编辑为指向性能方向的“最佳”,我将添加其他内容。

这个问题似乎是在数组不能是模块变量的前提下提出的。它们甚至可以是可分配/指针(延迟形状)的。

在下面的示例中,我将假设main 中的数组虚拟参数将被假定为形状。这只是为了让表格更简单;更改为显式形状是一个简单的扩展。

首先,就像我们在问题中设置mn一样,我们可以设置一个模块数组变量。

module test
  private     ! Have this as default
  public main ! But we do want a way in

  integer m, n
  real, allocatable :: matrixA(:,:)

contains
  ! In this subroutine we're making an assumption that the person asking isn't
  ! wholly correct in not wanting to assume shape.  But we can change that if
  ! required.  It's just a more natural thing if one can.
  subroutine main(matrix)               ! Note, not passing m, n
    real, intent(inout) :: matrix(:,:)  ! It's assumed shape

    m = SIZE(matrix,1)
    n = SIZE(matrix,2)

    matrixA = matrix  ! Copy in to the module's matrixA

    call foo()
    ! .... etc.

    matrix = matrixA  ! Copy back out to the dummy

  end subroutine main

  subroutine foo
    ! Here we have access to the module's matrixA
    ! And we do our stuff
  end subroutine foo

end module test

注意假设形状虚拟参数的使用和上面的讨论。为了避免在这个例子中复制,可以考虑使用指针是否合适。

使用副本,这不会是好的性能(假设数组是 biiig)。所以:

module test
  implicit none
  private
  integer m, n ! If we want these we'll have to set them somehow
  real, allocatable, public :: matrixA(:,:)

  public main

contains

  subroutine main()
    ! Stuff with matrixA host-associated
  end subroutine main

end module test

program hello
  use test, only : matrixA, main
  implicit none

  matrixA = ...  ! Set this: it's matrixA from the module
  call main
end program hello

当我们关心“性能”而不是“封装”时,这似乎是合理的事情。但是,由于具有封装、性能和减少参数传递,我们可以考虑在 main 内部使用工作子例程,而不是为 matrixA 使用模块变量。

 module test
 contains
   subroutine main(matrixA)
     real, intent(inout)  :: matrixA(:,:)

     call foo

    contains
     subroutine foo
       ! Here we have access to matrixA under host association
     end subroutine foo
   end subroutine main
 end module test

在许多情况下,我更喜欢最后一个,但不会说它是最好的,甚至在所有情况下都是首选。请特别注意,如果foo 已经有一个内部子程序,我们就会开始怀疑生活。

我觉得第一个比较极端,只是为了避免作为参数传递。

【讨论】:

  • 不值得编辑问题,但是:模块变量的一般延迟形状。有人可能认为模块中的matrixA 可以具有pointer 属性。
  • 第二个选择肯定更好,因为已知假设的形状效率较低。如果子程序被多次调用,性能损失很明显。另外,如果我不需要每次调用 main 时都将整个矩阵复制到模块中会更好。编辑:抱歉,但对于第二种方法,我仍然需要在“子子例程”中明确传递一些参数。
  • 传递界限(这很合理)是对我的示例的简单扩展。事实上,我将保持代码不变,因为这在说明中更清楚(我认为)。请注意,如果 m0n0 在内部案例中被传递,它们也将由主机关联提供。实际上,在main 中定义/传递给/关联的任何内容都可用于任何内部子程序(除非在那里使用了竞争名称)。后者是否回答您的评论编辑?
猜你喜欢
  • 1970-01-01
  • 2011-08-03
  • 2021-06-06
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-25
相关资源
最近更新 更多