4

プロジェクト euler の 14 番目の問題に関する投稿を書いているときに、VC9 と VC10 の動作の違いに遭遇しました。

次のコードは、VC9 では正常に実行されますが、VC10 では例外std::unordered_mapがスローされbad_allocます。奇妙なことは、例外から回復すると、将来の割り当てが成功することです (コンテナーのサイズは大きくなり続けます)。また、使用するboost::unordered_mapと、両方のコンパイラで正常に動作します。

実際のメモリ使用量に関しては、4GB RAM (使用中は 1.7) のマシンで実行しています。VC9 バージョンはタスクを完了する前に最大 810MB のメモリを取得し、VC10 バージョンは最大 658MB でクラッシュします。

これは VC10 のバグですか? 私は同じマシンで実行していますが、実行される作業量が同じである場合に、一方のバージョンでは一貫してメモリが不足し、もう一方のバージョンではメモリが不足する原因となるものは他にありますか?

<編集>
いくつかの詳細情報:例外が最初に発生するのは、スタックの深さ 1 で 7,718,688 を計算するときです (メインから長さだけの再帰はありません)。その後、キャッシュに追加される番号ごとに発生するようです。キャッシュには、例外が発生する前に 16,777,217 個の要素が含まれていました (によるとcache.size())。興味深いのは、insert失敗した場合でもキャッシュ サイズが 1 ずつ大きくなるため、強力な例外保証を提供していないように見えることです (§23.2.1.11 に違反しています)。
</編集>

コードは次のとおりです。

#include <iostream>
#include <unordered_map>

typedef std::unordered_map<_int64, int> cache_type;

_int64 collatz(_int64 i)
{
    return (i&1)? i*3+1 : i/2;
}

int length(_int64 n, cache_type& cache)
{
    if (n == 1)
        return 1;

    cache_type::iterator found = cache.find(n);
    if (found != cache.end())
        return found->second;
    int len = length(collatz(n), cache) + 1; 
    cache.insert(std::make_pair(n, len)); // this sometimes throws
    return len;
}

int main(int argc, char** argv)
{
    const int limit = 10000000;
    cache_type cache;
    std::pair<int, int> max = std::make_pair(0, 0);
    for (int i = 2; i <= limit; ++i) {
        int len = length(i, cache);
        if (len > max.second)
            max = std::make_pair(i, len);
    }

    std::cout<< "Number with longest orbit is " << max.first 
        << " with a lenght of " << max.second 
        << " cache size is " << cache.size() << std::endl;
}

<編集>
また、誰でもこの動作を再現できます。一度は消えた (そして再び現れた) ため、私の構成には何か特別なものがあるかもしれません。
</編集>

4

5 に答える 5

2

偶発的な場合もありますが、_SECURE_SCLの値を変更すると、説明した動作が発生します。

すなわち、コンパイル:

cl /EHa /MD /D_SECURE_SCL=1 /Ox /c t1.cpp
link /LIBPATH:"c:/Program Files/Microsoft Visual Studio 10.0/VC/lib" /LIBPATH:"C:/Program Files/Microsoft SDKs/Windows/v7.0A/Lib" t1.obj

クラッシュしますが、XP32ビットマシンで_SECURE_SCL=0の同じコマンドが実行されて完了します。_SECURE_SCLのmsdnページには、デバッグビルドが有効になっているが、リリースは有効になっていないと記載されています。これは、IDEでビルドしている場合に重要になる可能性があります。

于 2010-07-16T08:25:06.897 に答える
1

マップのハッシュ テーブルのサイズを変更する必要がある場合、1 つの要素を挿入すると、大量のメモリが割り当てられる可能性があります。マップは実行終了時に約0.5GBのようです。(上記の私のコメントを参照してください。)

ハッシュ テーブルを拡張する必要がある場合に、どれだけ拡張するかを決定するために何らかのヒューリスティックが使用されていると思われます。したがって、これは、ハッシュ テーブルがコピーされている間、古いデータと新しいデータに最大 1.5 GB を使用します。

したがって、プログラムがプロセス メモリ サイズの制限に達している可能性があります。(もう一度コメントを参照してください。) その場合、VC10 は VC9 よりも全体的にわずかに多くのメモリを使用し、プログラムの実行またはビルドごとにわずかに異なる量のメモリが割り当てられる可能性があります。そのため、VC10 は時々制限に達しますが、VC9 は制限に達しません。ヒットしたことはありません。

于 2010-07-11T05:35:10.237 に答える
0

_int64には、マップが割り当てで尊重しない可能性のある配置要件がありますか?

代わりにlonglongintを使用してみて、動作が変わるかどうかを確認してください。

于 2010-07-07T14:54:02.200 に答える
0

1 - EventLog をチェックして、プロセスが許可されたクォータを超えていることを示すイベントがあるかどうかを確認します。

2 - 32 ビット OS を使用している場合は、ユーザー スペース用に 3GB で起動してみてください。

3 - 別のアロケータが利用可能かどうかを確認します

4 - 9.0 と 10.0 の unordered_map を比較すると、人為的なサイズ リミッターが追加されている可能性が低い ("セキュリティ機能" :-)。x86 ビルドと x64 ビルドで値が異なるマクロにある可能性が最も高いでしょう。

5 - アロケータの周りに軽いラッパーを置き、各割り当てのサイズだけを出力してみてください。それはまた、それが実際にスローされているアロケーターなのか、それともその前のものなのかを教えてくれます。

6 - アロケーターがスローしている場合は、それから行われた実際の WinNT API 呼び出しを見てください (そして、9.0 との比較を再度行ってください)。

7 - 巨大なブロック (1 GB など) を事前に割り当ててみてください。

于 2010-07-14T11:55:42.110 に答える
0

への深く再帰的な呼び出しでスタックを吹き飛ばしていますlength()

于 2010-07-07T15:59:31.740 に答える