隐藏的全局变量是不良的编程实践吗?

5
我正在编写一些线性代数代码(使用Fortran 2003,但在Fortran 90或C中也会遇到同样的问题),需要一些工作向量进行计算。我处理这个问题的想法是创建一个私有的工作数组w(:,:),它是线性代数模块的“隐藏全局变量”,即在此讨论中定义的真正全局变量的可怕之处。
我将其想象为在黑板上解决一个大问题,并为解决问题的每个部分选择一个黑板区域。
按照这个类比,我还可以有一堆小白板:定义一个work_array数据类型,并根据需要将它们传递给求解器。(PETSc通过另一层抽象有效地使用了这种方法;一个solver是一个数据类型,包括一些用于方法的过程指针以及一些工作向量。)当从一个求解器嵌套调用到另一个时,这会变得有点复杂,所以我更喜欢第一种方式。它也不需要太多的误导。
对于哪种方法更好的编程实践,有什么想法吗?
编辑:我也不认为当我开始使用OpenMP时会有问题,因为我已经在这个代码的旧版本中这样做了。在问题设置之后,每个线程只访问其未知量的一部分,而不是其他线程的未知量。尽管如此,并发问题可能是不使用静态变量的一个很好的理由。
如果我每次调用求解器时都必须保持动态分配空间来存储临时数组(这经常发生),那么这不会产生很多开销吗?

2
如果有多个线程同时尝试访问它,会怎么样? - Mysticial
1
什么是好处?如果工作空间很小,就让它自动化。如果非常大,根据需要使用malloc和free。 - R.. GitHub STOP HELPING ICE
3个回答

6
如果在工作空间中进行任何非平凡计算,则mallocfree的成本将被分配空间中执行的计算成本所支配,并且开销将近似为零。只有在缓冲区上执行的工作量非常小,以至于获取缓冲区的时间可能占主导地位(或者至少可能无法被另一个术语所支配)时,避免分配才有意义作为优化策略。这种情况最常见的情况是构建字符串。
另一方面,全局状态有很多成本:
  1. 它排除了多线程使用。
  2. 如果状态需要在多个调用之间持续存在,则排除库使用(由于它们可能会互相覆盖工作,因此该库不能同时被程序的多个部分使用)。
  3. 它排除了递归/可重入使用。
  4. 每当链接库时,它都会使用内存,即使从未调用函数也是如此。
  5. 即使您努力解决其中的一些问题,它仍然是一种严重的代码异味,因此是人类时间的成本,即它会浪费下一个阅读您的代码的人的时间,而他们尝试确定全局状态是否实际上在代码上引入了任何错误或使用限制。

5

"隐藏的全局变量"(在C语言中被称为static)的最大危险在于编写并发程序时。一旦进入多线程领域,单个黑板不再足够:每个线程都需要自己的黑板。对于这种情况,动态分配更合适。如果不考虑多线程,模块作用域的“隐藏全局”变量完全可以使用。


1
关于分配成本:您可以拥有一个包含所有工作数组的派生类型(在您的情况下是数组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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接