4

十分な(仮想?)メモリを使用できるように見えるのに、なぜstd::bad_alloc例外が発生するのかを理解しようとしています。基本的に、素数ジェネレーター(Eratosthenes sieve(まだセグメント化されていません))があり、インジケーター配列のboolを新しくし、コマンドラインで指定した範囲内で見つけた素数のintを新しくします。

私は1GBのRAM(これのいくつかは私のOS(ubuntu 10.04)によって占有され、おそらくヒープメモリとして利用できないものもあります(私はここで間違っていますか?))と2.8GBのスワップスペース(それは自動だったと思います)を持っていますUbuntuをインストールするときに設定してください)

600000000の上限を設定すると、インジケーター配列に0.6 GBのメモリが必要になり、素数配列に約30000000 * 4バイト(500000000未満の素数が26355867ある場合は、見積もりをわずかに上回ります)といくつかの変数が必要になります。あちこち; これは、約.72(+無視できる)GBのメモリを要求していることを意味します。これは、使用可能なスワップスペースでカバーする必要があると思います(何かに触れると、プログラムが途方もなく遅くなることを認識しています)。しかし、私はstd::bad_allocsを取得しています。

誰かが私がここで欠けているものを指摘できますか?(最後のエラーを貼り付ける前にlong long intをintに変更した最後の1つは、セグメンテーション違反でした(ただし、私の数値は2 ^ 31をはるかに下回っているため、オーバーフローしている場所がわかりません)-それでもその1つを把握しようとしています)。

私のコードは次のとおりです(そして、より迅速なアルゴリズムなどについての私自身の調査の利点を奪うことなく、ここでコードの改善に感謝します!(つまり、私が主要なノーノーをコミットしている場合))

main.cpp

#include <iostream>
#include <cmath>
#include "Prime.hpp"
#include <ctime>
#include <stdio.h>
#include <cstring>

//USAGE: execute program with the nth prime you want and an upper bound for finding primes --too high may cause bad alloc
int main(int argc, const char *argv[])
{
    int a = strlen(argv[1]);
    clock_t start = clock();
    if(argc != 2)
    {
        std::cout << "USAGE: Enter a positive inputNumber <= 500000000.\n"
          << "This inputNumber is an upper bound for the primes that can be found\n";
        return -1;
    }
    const char* primeBound = argv[1];
    int inputNum = 0;
    for(int i = 0; i < strlen(argv[1]); i++)
    {
    if(primeBound[i] < 48 || primeBound[i] > 57 || primeBound[0] == 48)
    {
        std::cout << "USAGE: Enter a positive inputNumber <= 500000000.\n"
              << "This inputNumber is an upper bound for the primes that can be found\n";
        return -1;
    }
    inputNum = (int)(primeBound[i]-48) + (10 * inputNum);
    }
    if(inputNum > 600000000)//getting close to the memory limit for this machine (1GB - memory used by the OS):
                   //(each bool takes 1 byte and I'd be asking for more than 500 million of these 
                   //and I'd also asking for over 100000000 bytes to store the primes > 0.6 GB)
    {
    std::cout << "USAGE: Enter a positive inputNumber <= 500000000.\n"
          << "This inputNumber is an upper bound for the primes that can be found\n";
        return -1;
    }

    Prime p(inputNum);
    std::cout << "the largest prime less than " << inputNum << " is: " << p.getPrime(p.getNoOfPrimes()) << "\n";
    std::cout << "Number of primes: " << p.getNoOfPrimes() << "\n";
    std::cout << ((double)clock() - start) / CLOCKS_PER_SEC << "\n";
  return 0;
}

Prime.hpp

#ifndef PRIME_HPP
#define PRIME_HPP
#include <iostream>
#include <cmath>

class Prime
{
    int lastStorageSize;
    bool* primeIndicators;
    int* primes;
    int noOfPrimes;

    void allocateIndicatorArray(int num);
    void allocatePrimesArray();
    void generateIndicators();
    void generatePrimeList();
    Prime(){}; //forcing constructor with param = size

    public:
        Prime(int num);
    int getNoOfPrimes();
    int getPrime(int nthPrime);
    ~Prime(){delete [] primeIndicators; delete [] primes;}
};

#endif

Prime.cpp

#include "Prime.hpp"
#include <iostream>

//don't know how much memory I will need so allocate on the heap
void Prime::allocateIndicatorArray(int num)
{
    try
    {
        primeIndicators = new bool[num];
    }
    catch(std::bad_alloc ba)
    {
    std::cout << "not enough memory :[";
    //if I'm looking for a particular prime I might have over-allocated here anyway...might be worth
    //decreasing num and trying again - if this is possible!
    }
    lastStorageSize = num;
}

void Prime::allocatePrimesArray()
{
    //could probably speed up generateIndicators() if, using some prime number theory, I slightly over allocate here
    //since that would cut down the operations dramatically (a small procedure done many times made smaller)
    try
    {
        primes = new int[lastStorageSize];
    }
    catch(std::bad_alloc ba)
    {
    std::cout << "not enough memory :[";
    //if I'm looking for a particular prime I might have over-allocated here anyway...might be worth
    //decreasing num and trying again - if this is possible!
    }
}
void Prime::generateIndicators()
{
    //first identify the primes -- if we see a 0 then start flipping all elements that are multiples of i starting from i*i (these will not be prime)
    int numPrimes = lastStorageSize - 2; //we'll be starting at i = 2 (so numPrimes is at least 2 less than lastStorageSize)
    for(int i=4; i < lastStorageSize; i+=2)
    {
        primeIndicators[i]++; //dispense with all the even numbers (barring 2 - that one's prime)
    numPrimes--;
    }
    //TODO here I'm multiple counting the same things...not cool >;[
    //may cost too much to avoid this wastage unfortunately
    for(int i=3; i < sqrt(double(lastStorageSize)); i+=2) //we start j at i*i hence the square root
    {
        if(primeIndicators[i] == 0)
    {
            for(int j = i*i; j < lastStorageSize; j = j+(2*i)) //note: i is prime, and we'll have already sieved any j < i*i
        {
        if(primeIndicators[j] == 0)
        {
            numPrimes--;//we are not checking each element uniquely yet :/
                    primeIndicators[j]=1;
        }
        }
    }
    }
    noOfPrimes = numPrimes;
}
void Prime::generatePrimeList()
{
    //now we go and get the primes, i.e. wherever we see zero in primeIndicators[] then populate primes with the value of i
    int primesCount = 0;
    for(int i=2;i<lastStorageSize; i++)
    {
        if(primeIndicators[i] == 0)
        {
        if(i%1000000 = 0)
        std::cout << i << " ";
            primes[primesCount]=i;
            primesCount++;
        }
    }
}
Prime::Prime(int num)
{
    noOfPrimes = 0;
    allocateIndicatorArray(num);
    generateIndicators();
    allocatePrimesArray();
    generatePrimeList();
}

int Prime::getPrime(int nthPrime)
{
    if(nthPrime < lastStorageSize)
    {
        return primes[nthPrime-1];
    }
    else
    {
    std::cout << "insufficient primes found\n";
    return -1;
    }
}

int Prime::getNoOfPrimes()
{
    return noOfPrimes;
}

私が読んでいる間、誰かがこれについて何か洞察を得ましたか?

編集何らかの理由で、noOfPrimeではなくlastStorageSizeintsを使用して素数リストの更新を開始することにしました。それを見つけてくれたDavidFischerに感謝します!

上限として600000000を超えることができるようになりました

4

4 に答える 4

4

プログラム内で使用できるメモリの量は、1)使用可能な仮想メモリ、2)使用可能なアドレス空間の2つのうち少ない方によって制限されます。

フラットメモリモデルを備えたプラットフォームで32ビット実行可能ファイルとしてプログラムをコンパイルする場合、単一プロセスのアドレス可能なスペースの絶対制限は4GBです。この状況では、使用可能なスワップスペースの量はまったく関係ありません。まだ多くの空きスワップ領域がある場合でも、フラットメモリの32ビットプログラムに4GBを超える容量を割り当てることはできません。さらに、これらの4GBの使用可能なアドレスの大部分は、システムのニーズのために予約されます。

このような32ビットプラットフォームでは、一度に複数のプロセスを実行できるため、大量のスワップスペースを割り当てることは理にかなっています。ただし、特定のプロセスごとに4GBのアドレス空間の障壁を克服することはできません。

基本的に、これは電話番号の可用性の問題と考えてください。一部の地域で7桁の電話番号が使用されている場合、その地域で利用可能な7桁の電話番号がなくなると、その地域で利用可能な電話を増やすことは意味がなくなります。 -使用できなくなります。スワップスペースを追加することで、基本的に「電話を製造」することになります。しかし、あなたはすでに利用可能な「電話番号」を使い果たしています。

もちろん、フラットメモリモデルの64ビットプラットフォームでも同じ問題が正式に存在します。ただし、64ビットプラットフォームのアドレス空間は非常に大きいため、ボトルネックではなくなりました(「64ビットで十分なはずです」:))

于 2012-11-26T01:06:31.867 に答える
1

プログラムのメモリ使用量は非常に簡単に分析できるため、メモリレイアウトを完全に固定するだけです。動的に何も割り当てないでください。を使用std::bitsetして固定サイズのビットベクトルを取得し、それをグローバル変数にします。

std::bitset< 600000000 > indicators; // 75 MB

これはディスク上のスペースを占有しません。配列に沿って進むと、OSはゼロのページを割り当てるだけです。そして、それは各ビットをよりよく利用します。

もちろん、偶数の素数が1つしかないにもかかわらず、半分のビットは偶数を表します。そのようなものを最適化するいくつかのプライムジェネレータがあります。

ちなみに、new可能であれば明示的に書き込むことは避け、コンストラクターから関数を呼び出さstd::bad_allocないようにし、オブジェクトが無効な状態に構築されるのを避けるためにを再スローすることをお勧めします。

于 2012-11-26T01:21:34.507 に答える
1

ふるいを割り当てるとき、

void Prime::allocateIndicatorArray(int num)
{
    try
    {
        primeIndicators = new bool[num];
    }
    catch(std::bad_alloc ba)
    {
    std::cout << "not enough memory :[";
    }
    lastStorageSize = num;
}

に設定lastStorageSizeするnumと、素数の指定された境界になります。その後、あなたはそれを変更することはありません、そして

void Prime::allocatePrimesArray()
{
    try
    {
        primes = new int[lastStorageSize];
    }
    catch(std::bad_alloc ba)
    {
    std::cout << "not enough memory :[";
    }
}

要素のint配列を割り当ててみてください。lastStorageSize

numが約5億の場合、それはあなたが要求する約2GBです。bad_allocオペレーティングシステム/オーバーコミット戦略によっては、実際にはほんの一部のスペースしか必要としない場合でも、簡単に発生する可能性があります。

ふるい分けが終了したら、noOfPrimes見つかった素数の数に設定します。その数を使用して素数配列を割り当てます。

于 2012-11-26T01:28:43.793 に答える
1

最初の質問は、「他にどのようなプロセスが実行されているか」です。2.87 GBのスワップ領域は、実行中のすべてのプロセス間で共有されます。プロセスごとではありません。そして率直に言って、現代のシステムでは、2.8GBは私にはかなり低いように聞こえます。2GB未満のRAMと4GBのスワップを備えた最近のバージョンのWindowsまたはLinuxを実行しようとはしませんでした。(少なくともUbuntuディストリビューションの最近のバージョンのLinuxは、メモリを大量に消費する多くのデーモンを起動するようです。)top仮想メモリのサイズでソートして、他のプロセスがどれだけ取っているかを確認することをお勧めします。 。

cat /proc/meminfoまた、実際に使用されているものに関する多くの貴重な情報を提供します。(私のシステムでxtermbash、プラスFirefoxで、8GBのシステムでは3623776 kBしか空きがありません。使用されたと見なされるメモリの一部は、おそらくディスクキャッシングのようなものであり、アプリケーションがあればシステムを縮小できます。メモリを要求します。)

次に、セグメンテーション違反についてです。デフォルトでは、Linuxは常に割り当ての失敗を正しく報告するとは限りません。それはしばしば嘘をつき、あなたが記憶を持っていないのに、あなたは記憶を持っているとあなたに告げます。試してみてくださいcat /proc/sys/vm/overcommit_memory。ゼロが表示されている場合は、変更する必要があります。この場合は、試してくださいecho 2 > /proc/sys/vm/overcommit_memory(rcファイルの1つでこれを実行してください)。/proc/sys/vm/overcommit_ratioから信頼できる動作を取得するには、も変更する必要がある場合があります sbrk(両方mallocoperator new依存します)。

于 2012-11-26T01:31:05.257 に答える