5

私はいくつかの線形代数コードを書いています(Fortran 2003では、Fortran 90またはCでも同じ問題です)。これには、計算を行うためにいくつかの作業ベクトルが必要です。これを処理するための私のアイデアは、次のような作業配列を作成することですw(:,:)。は線形代数モジュール専用です。つまり、真のグローバル変数がひどい理由についてのこの説明で定義されている「隠しグローバル」です。

これは黒板で解決すべき大きな問題があると想像します。問題の各部分について、黒板の領域を選択して解決します。

そのアナロジーに沿って、私はたくさんの小さなホワイトボードを持つこともできます。work_arrayデータ型を定義し、必要に応じてそれらをソルバーに渡します。(PETScは、抽象化の別のレイヤーを通じてこのアプローチを効果的に使用します。asolverは、使用されるメソッドへのいくつかのプロシージャポインターと、いくつかの作業ベクトルを含むデータ型です。)1つのソルバーから別のソルバーへのネストされた呼び出しがある場合、これはダニは複雑なので、最初の方法の方が好きです。また、それほど多くの誤った方向性を必要としません。

どのアプローチがより良いプログラミングの練習に役立つかについて何か考えはありますか?

編集:OpenMPを使い始めたときにも問題になるとは思いません。これは、このコードの古い形ですでに行っています。問題が設定された後、各スレッドは未知の部分にのみアクセスし、他のスレッドの部分にはアクセスしません。それでも、並行性の問題は、静的変数を一般的に使用しない理由としておそらく適切です。

ソルバーを呼び出すたびにスクラッチ配列に動的にスペースを割り当て続ける必要がある場合、これは多くの場合、多くのオーバーヘッドが発生しませんか?

4

3 に答える 3

6

作業スペースで重要な計算を行う場合、mallocおよびfreeのコストは、割り当てられたスペースで実行される計算のコストによって支配され、オーバーヘッドはほぼゼロになります。最適化戦略として割り当てを回避することが理にかなっているのは、バッファーで実行される作業量が非常に少ないため、バッファーを取得する時間が支配的である(または少なくとも別の用語で支配されない可能性がある)場合のみです。これが発生する可能性のある主な状況は、文字列の作成です。

一方、グローバルな状態には多くのコストがかかります。

  1. マルチスレッドでの使用はできません。
  2. 状態が複数の呼び出し間で持続する必要がある場合、ライブラリーの使用ができなくなります(ライブラリーは、プログラムの複数の部分で一度に使用できなくなります。これは、ライブラリーが相互の作業を妨害する可能性があるためです)。
  3. 再帰的/再入可能な使用を排除します。
  4. 関数が呼び出されない場合でも、ライブラリがリンクされているときは常にメモリを使用します。
  5. これらの問題のいくつかを回避するために努力しても、それは深刻なコードの臭いであり、したがって人的時間のコストになります。つまり、グローバル状態がコードにバグや使用制限を実際に導入します。
于 2013-03-26T04:58:42.567 に答える
5

「隠されたグローバル」(Cの世界ではそれらは呼ばれstaticます)の最大の危険は、並行プログラムを作成するときに発生します。マルチスレッドに挑戦すると、単一の黒板を持つだけではもはや十分ではありません。各スレッドには独自の黒板が必要です。このような状況では、動的割り当てがより適切です。マルチスレッドについて心配しない場合は、モジュールスコープの「非表示のグローバル」変数を使用することはまったく問題ありません。

于 2013-03-26T00:04:09.893 に答える
1

割り当てコストについて:すべての作業配列(この場合は配列)を含む派生型を使用できますw(:,:)。初期化呼び出しを1回実行して、それらを正しいサイズに割り当ててから、割り当てられた配列を含む派生型をできるだけ頻繁にソルバーに渡すことができます。これは次のような考え方です。

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 に答える