2010-12-02 88 views
5

OpenMPを使用すると、 "post MT section"メッセージの後のループで、32ビットマシン(std :: bad_allocをスローする)のメモリが不足しますが、#pragma for OpenMPはコメントアウトされているので、コードは正常終了まで実行されるので、メモリが並列スレッドで割り当てられても、スレッドは正しく解放されず、メモリが不足するようです。OpenMPを使用しているときにメモリリークが発生する

以下のメモリ割り当ておよび削除コードに問題があるかどうか、またはこれはgcc v4.2.2またはOpenMPのバグですか?私はgcc v4.3も試してみたが、同じ失敗があった。

int main(int argc, char** argv) 
{ 
    std::cout << "start " << std::endl; 

    { 
      std::vector<std::vector<int*> > nts(100); 
      #pragma omp parallel 
      { 
        #pragma omp for 
        for(int begin = 0; begin < int(nts.size()); ++begin) { 
          for(int i = 0; i < 1000000; ++i) { 
            nts[begin].push_back(new int(5)); 
          } 
        } 
      } 

    std::cout << " pre delete " << std::endl; 
      for(int begin = 0; begin < int(nts.size()); ++begin) { 
        for(int j = 0; j < nts[begin].size(); ++j) { 
          delete nts[begin][j]; 
        } 
      } 
    } 
    std::cout << "post MT section" << std::endl; 
    { 
      std::vector<std::vector<int*> > nts(100); 
      int begin, i; 
      try { 
       for(begin = 0; begin < int(nts.size()); ++begin) { 
        for(i = 0; i < 2000000; ++i) { 
          nts[begin].push_back(new int(5)); 
        } 
       } 
      } catch (std::bad_alloc &e) { 
        std::cout << e.what() << std::endl; 
        std::cout << "begin: " << begin << " i: " << i << std::endl; 
        throw; 
      } 
      std::cout << "pre delete 1" << std::endl; 

      for(int begin = 0; begin < int(nts.size()); ++begin) { 
        for(int j = 0; j < nts[begin].size(); ++j) { 
          delete nts[begin][j]; 
        } 
      } 
    } 

    std::cout << "end of prog" << std::endl; 

    char c; 
    std::cin >> c; 

    return 0; 
} 
+0

インテルコンパイラでビルドされたWindowsでこれを実行すると、32ビットプロセスの2GBの制限に達するため、最初のループで割り当てが失敗します。 OpenMPのオーバーヘッドが、あなたのプラットフォーム上の制限を超えてプロセスをプッシュするだけで済む可能性はありますか? – Scott

+0

@Scott Danahyテストケースを変更してすべての割り当てを半分にするようにしてください。このテストでは4 GBの制限がありました。 – WilliamKF

答えて

3

この問題は、OpenMPを使用せずにpthreadを使用しているところにあります。マルチスレッドの場合の余分なメモリ消費は、標準メモリアロケータの典型的な動作のようです。 Hoardアロケータに切り替えると、余分なメモリ消費がなくなります。

0

なぜあなたは、内側のベクトルメンバーとしてint*を使用していますか?これは非常に無駄です.のエントリごとに、4バイト(厳密にはsizeof(int))のデータとヒープ制御構造の2-3倍のデータがあります。これをちょうどvector<int>を使用して試してみてください。

私はOpenMPのエキスパートではありませんが、この使用法は非対称性が奇妙なようです。ベクトルをパラレルセクションで埋め、非パラレルコードでクリアします。それが間違っているかどうかは分かりませんが、間違っていると感じます。

+0

@Steve Townsendこのテストケースは、実際に使用されるコードではなく、その問題を示すためのものです。各エントリの5つのintの配列は、リークを示すためにメモリを消費するのに役立ちます。コードをよりメモリ効率の良いものにすると、さらに多くのメモリが消費されるまで、失敗が遅れることになります。 – WilliamKF

+0

@WilliamKF - わかりました。私はOpenMPの教祖がコメントする必要があると思います。 *メモリ割り当てや削除に関する問題が*質問されていますが、私にとってはRAIIが望ましいと思われます。あなた自身の答えに興味があるので、あなたのqに+1を加えています。 –

+0

このコードは問題を実証するためのものですから、少し面倒ですが、OpenMP構造体の外側でクリーンアップとポストプロセッシングを行うことは珍しいことではありません。 –

3

最初のOpenMPループを1000000から2000000に変更すると、同じエラーが発生します。これは、メモリー不足の問題がOpenMPスタック限界であることを示します。

あなたはまた、OpenMPの環境変数OMP_STACKSIZEを変更し、100メガバイト以上に設定することができます

ulimit -s unlimited 

とbashで無制限にするOpenMPのスタックリミットを設定してみてください。

UPDATE 1:私は、私は、メインスレッドでI = 1574803でメモリエラーを取得し、その後

{ 
    std::vector<std::vector<int*> > nts(100); 
    #pragma omp for schedule(static) ordered 
    for(int begin = 0; begin < int(nts.size()); ++begin) { 
     for(int i = 0; i < 2000000; ++i) { 
      nts[begin].push_back(new int(5)); 
     } 
    } 

    std::cout << " pre delete " << std::endl; 
    for(int begin = 0; begin < int(nts.size()); ++begin) { 
     for(int j = 0; j < nts[begin].size(); ++j) { 
      delete nts[begin][j] 
     } 
    } 
} 

への最初のループを変更します。

更新2:Intelコンパイラを使用している場合は、次のコードをコードの先頭に追加して、余分なオーバーヘッドに十分なメモリがあれば問題を解決できます。

std::cout << "Previous stack size " << kmp_get_stacksize_s() << std::endl; 
kmp_set_stacksize_s(1000000000); 
std::cout << "Now stack size " << kmp_get_stacksize_s() << std::endl; 

UPDATE 3:あなたは、いくつかの数値計算を実行している場合は完全にするために、別のメンバーが言及したように、それは単一新しいフロートですべてを事前に割り当てることが最善である[1000000]代わりに1000000を行うためのOpenMPを使用します割り当て。これは、オブジェクトの割り当てにも適用されます。

+0

最初のループを2000000に変更すると、新しいメモリの割り当てに失敗したときに、プロセスに割り当てられた合計サイズはいくらですか? – Scott

+0

i = 1574803クラッシュしたとき。私の更新1を参照してください。 –

+0

@Dat Chu Intelコンパイラを使用せずに(つまりgccを使用して)、kmp_set_stacksize_s()がgccで利用できないため、OMP_STACKSIZEのenv var設定は行く方法ですか? – WilliamKF

関連する問題