1

以下は、よく知られている LAPACK 数値ライブラリの Fortran ZHEEVR ルーチンの C ラッパーです。

void zheevr(char jobz, char range, char uplo, int n, doublecomplex* a, int lda, double vl, double vu, int il, int iu, double abstol, double* w, doublecomplex* z, int ldz, int* info)
{
    int m;
    int lwork = -1;
    int liwork = -1;
    int lrwork = -1;
    int* isuppz = alloc_memory(sizeof(int) * 2 * n);
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, small_work_doublecomplex, &lwork, small_work_double, &lrwork, small_work_int, &liwork, &info);
    lwork = (int) small_work_doublecomplex[0].real;
    liwork = small_work_int[0];
    lrwork = (int) small_work_double[0];
    doublecomplex* work = alloc_memory(sizeof(doublecomplex) * lwork);
    double* rwork = alloc_memory(sizeof(double) * lrwork);
    int* iwork = alloc_memory(sizeof(int) * liwork);
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, work, &lwork, rwork, &lrwork, iwork, &liwork, info);
    free(iwork);
    free(rwork);
    free(work);
    free(isuppz);
}

この関数は、私のアプリケーションで何十万回も呼び出され、同じ行列サイズの複素行列 "a" (パラメーター名はこの関数の Fortran 規則に従います) を対角化します。対角化された行列は同じ構造になるため、作業配列のサイズはほとんどの場合同じになると思います。私の質問は次のとおりです。

  1. alloc/free (「alloc_memory」は glibc の malloc の単純なラッパーです) 呼び出しを繰り返すと、パフォーマンスが低下する可能性がありますか?
  2. フリーの順番は関係ありますか?最後に割り当てられた配列を最初に解放するべきですか、それとも最後に解放すべきですか?
4

5 に答える 5

5
  • C99は使えますか?(答え: はい、既に C99 表記法を使用しています - 必要に応じて変数を宣言します。)
  • 配列のサイズは適切ですか (大きすぎないか)。

両方の答えが「はい」の場合は、VLA の可変長配列の使用を検討してください。

void zheevr(char jobz, char range, char uplo, int n, doublecomplex* a, int lda, double vl, double vu, int il, int iu, double abstol, double* w, doublecomplex* z, int ldz, int* info)
{
    int m;
    int lwork = -1;
    int liwork = -1;
    int lrwork = -1;
    int isuppz[2*n];
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, small_work_doublecomplex, &lwork, small_work_double, &lrwork, small_work_int, &liwork, &info);
    lwork = (int) small_work_doublecomplex[0].real;
    liwork = small_work_int[0];
    lrwork = (int) small_work_double[0];
    doublecomplex work[lwork];
    double rwork[lrwork];
    int iwork[liwork];
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, work, &lwork, rwork, &lrwork, iwork, &liwork, info);
}

VLA を使用する利点の 1 つは、ユーザーが解放を行う必要がないことです。

(未テストのコード!)

于 2009-06-07T16:48:21.863 に答える
5

1) はい、できます。

2) 健全な libc は、free() の順序を気にするべきではありません。パフォーマンスに関しても重要ではありません。

この関数からメモリ管理を削除することをお勧めします。そのため、呼び出し元は行列のサイズと割り当てられた一時バッファーを提供します。この関数が同じサイズのマトリックスの同じ場所から呼び出されると、malloc の数が大幅に削減されます。

于 2009-06-07T16:17:35.590 に答える
2

それは確かにパフォーマンスに影響を与えます - タイミングによってのみ確実に知ることができます。ほとんどの割り当てを回避するバージョンを作成するには、静的ポインターに割り当て、別の静的整数でサイズを記憶します。次の呼び出しで同じサイズが使用される場合は、前回割り当てられたものを再利用します。サイズが変更されたために新しいマトリックスを作成する必要がある場合にのみ、何かを解放してください。

このソリューションは、シングルスレッド コードにのみ適していることに注意してください。

于 2009-06-07T16:13:40.923 に答える
1

大丈夫。プロファイラーの回答はすぐに得られます。AMD マシンをお持ちの場合は、無料の AMD の CodeAnalyst を強くお勧めします。

メモリの問題については、この場合、ローカル メモリ管理を使用できると思います。この関数に割り当てることができるメモリの最大数を決定するだけです。次に、静的バッファーを宣言し、コンパイラーがスタックを処理する方法と少し似た方法で、静的バッファーを操作します。VirtualAlloc でこのようなラッパーを一度実行しましたが、非常に高速です。

于 2009-06-07T16:14:52.937 に答える
1

同じサイズのアイテムを何十万回も割り当てている場合は、オブジェクトのヒープを維持して (他の割り当てられたメモリへのポインターが含まれていないなど、比較的単純なように見えるため)、独自のヒープに解放しないでください。 (または実際にスタック)?

ヒープは glib malloc を使用して新しいオブジェクトを遅延して割り当てることができますが、解放するときはアイテムをヒープにプッシュするだけです。割り当てる必要がある場合、解放されたオブジェクトが利用可能であれば、そのオブジェクトを割り当てることができます。

これにより、割り当ての複数回の呼び出しも節約され (割り当てを行う必要がなく、ルーチンが malloc を複数回呼び出すように見えるため)、少なくとも再利用されたメモリでの断片化を (ある程度) 回避できます。 . もちろん、最初の割り当て (およびこのメモリを拡張する必要があるときにプログラムが実行されているときのその他の割り当て) によって断片化が発生する可能性がありますが、これが本当に心配な場合は、いくつかの統計を実行して、メモリの平均/最大/典型的なサイズを見つけることができます。実行中にヒープし、プログラムの起動時にこれを一度に事前に割り当てて、断片化を回避します。

于 2009-06-07T16:23:22.563 に答える