5

我正在编写一些线性代数代码(在 Fortran 2003 中,但在 Fortran 90 或 C 中将是相同的问题),它需要一些工作向量来进行计算。我处理这个问题的想法是制作一个工作w(:,:)数组是线性代数模块私有的,即在讨论为什么真正的全局变量很糟糕时定义的“隐藏的全局” 。

我想这是在黑板上有一个大问题要解决,对于问题的每个部分,你选择黑板上的一个区域来解决它。

按照这个类比,我还可以有一堆小白板:定义一个work_array数据类型并根据需要将它们传递给求解器。(PETSc 通过另一个抽象层有效地使用了这种方法;asolver是一种数据类型,其中包括一些指向所用方法的过程指针以及一些工作向量。)当从一个求解器到另一个求解器存在嵌套调用时,这会得到有点复杂,所以我更喜欢第一种方式。它也不需要太多的误导。

关于哪种方法有助于更好的编程实践的任何想法?

编辑:当我开始使用 OpenMP 时,我也不认为这会是一个问题,我已经在这段代码的旧版本中完成了。在设置问题后,每个线程仅访问其未知数部分,而不访问其他线程的未知数部分。尽管如此,并发问题可能是一般不使用静态变量的一个很好的理由。

如果每次调用求解器时都必须为临时数组动态分配空间,这通常不会产生很多开销吗?

4

3 回答 3

6

如果您在工作空间中进行任何重要的计算,则成本将由在分配空间中执行的计算成本支配,malloc并且free开销将近似为零。避免分配作为一种优化策略唯一有意义的情况是,在缓冲区上执行的工作量非常小,以至于获取缓冲区的时间可能占主导地位(或者至少,可能无法由另一个项主导)。可能发生这种情况的主要情况是构建字符串。

另一方面,全局状态有很多成本:

  1. 它排除了多线程使用。
  2. 如果该状态需要在多次调用之间保持不变,则它会阻止库的使用(该库一次不能被程序的多个部分使用,因为它们可能会破坏彼此的工作)。
  3. 它排除了递归/重入使用。
  4. 每当链接库时,它都会使用内存,即使从未调用过函数。
  5. 即使您努力解决其中一些问题,这也是一种严重的代码异味,因此会耗费人力时间,也就是说,它会浪费下一个阅读您的代码的人在尝试确定全局状态是否为实际上在代码上引入了任何错误或使用限制。
于 2013-03-26T04:58:42.567 回答
5

static当您编写并发程序时,“隐藏的全局变量”(在 C 世界中它们被称为 )的最大危险出现了。一旦你开始涉足多线程,只有一块黑板就不够了:每个线程都需要自己的。对于这样的情况,动态分配更合适。如果您不担心多线程,那么拥有一个模块范围的“隐藏的全局”变量是非常好的。

于 2013-03-26T00:04:09.893 回答
1

至于分配成本:您可以拥有一个包含所有工作数组的派生类型(在您的情况下为 array w(:,:))。您可以进行一个初始化调用,将它们分配给正确的大小,然后尽可能频繁地将包含已分配数组的派生类型传递给求解器,遵循以下精神:

module test
   implicit none

    type :: buffer
        integer, allocatable :: work(:,:)
    end buffer

contains

    subroutine init(mybuffer, whatever_else_you_need_for_determinig_allocation_size)
        type(buffer), intent(out) :: mybuffer

         allocate(mybuffer%work(dim1, dim2))
     end subroutine init

     subroutine solver(mybuffer, whatever_else_you_need_for_the_solver)
         type(buffer), intent(inout) :: mybuffer

          ! You can access mybuffer%work here as it is allocated

      end subroutine solver

end module test

但正如已经指出的那样,分配成本相对于您在求解器中花费的成本通常可以忽略不计。

于 2013-03-26T07:18:35.727 回答