web-dev-qa-db-ja.com

c ++が割り当てているメモリの量を理解する

私はc ++でヒープに割り当てられているメモリの量をよりよく理解しようとしています。私は基本的にいくつかの2Dベクトルを埋める以外に何もしない小さなテストプログラムを書きました。私はこれをLinux 64ビットで実行していますVMおよびメモリのプロファイルを作成するためにvalgrindのMassifツールを使用します。

私がこのテストを実行している環境:Linux VM Win10のVirtualBoxで実行中。VM構成:基本メモリ:5248MB、4CPU、上限100% 、ディスクタイプVDI(動的に割り当てられたストレージ)。

c ++メモリプロファイリングテストプログラム:

_/**
 * g++ -std=c++11 test.cpp -o test.o
 */

#include <string>
#include <vector>
#include <iostream>

using namespace std;

int main(int argc, char **arg) {
    int n = stoi(arg[1]);
    vector<vector<int> > matrix1(n);
    vector<vector<int> > matrix2(n);
    vector<vector<int> > matrix3(n);
    vector<vector<int> > matrix4(n);
    vector<vector<int> > matrix5(n);
    vector<vector<int> > matrix6(n);
    vector<vector<int> > matrix7(n);
    vector<vector<int> > matrix8(n);

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix1[i].Push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix2[i].Push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix3[i].Push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix4[i].Push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix5[i].Push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix6[i].Push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix7[i].Push_back(j);
        }
    }

    for (int i=0; i<n; ++i) {
        for (int j=0; j<n; ++j) {
            matrix8[i].Push_back(j);
        }
    }
}
_

nのさまざまな値でメモリプロファイルを抽出するために次のbashスクリプトを実行します(test.oは上記のプログラムで、g ++ -std = c ++ 11でコンパイルされています。g++はバージョン5.3.0です)

_valgrind --tool=massif --massif-out-file=massif-n1000.txt ./test.o 250
valgrind --tool=massif --massif-out-file=massif-n1000.txt ./test.o 500
valgrind --tool=massif --massif-out-file=massif-n1000.txt ./test.o 1000
valgrind --tool=massif --massif-out-file=massif-n2000.txt ./test.o 2000
valgrind --tool=massif --massif-out-file=massif-n4000.txt ./test.o 4000
valgrind --tool=massif --massif-out-file=massif-n8000.txt ./test.o 8000
valgrind --tool=massif --massif-out-file=massif-n16000.txt ./test.o 16000
valgrind --tool=massif --massif-out-file=massif-n32000.txt ./test.o 32000
_

これにより、次の結果が得られます。

_|--------------------------------|
| n     | peak heap memory usage |
|-------|------------------------|
| 250   | 2.1 MiB                |         
| 500   | 7.9 MiB                |
| 1000  | 31.2 MiB               |
| 2000  | 124.8 MiB              |
| 4000  | 496.5 MiB              |
| 8000  | 1.9  GiB               |
| 16000 | 6.2 GiB                |
| 32000 | 6.1 GiB                |
|--------------------------------|
_

各行列のサイズはn ^ 2で、合計8つの行列があるため、メモリ使用量はf(n) = 8 * n^2前後になると予想しました。

質問1n = 250からn = 8000まで、n * = 2でメモリ使用量が多かれ少なかれ4倍になるのはなぜですか?

N = 16000からn = 32000までは、valgrindが実際にメモリの減少を報告するため、非常に奇妙なことが起こっています。

質問2n = 16000とn = 32000の間で何が起こっているか、どのようにしてヒープメモリが少なくなる可能性があるのか​​、理論的にはより多くのデータが必要です割り当てられた?

以下のn = 16000およびn = 32000のmassif-visualizer出力を参照してください。

n=16000n=32000

22
Gio

1)行列ベクトルのサイズ(およびそのメモリフットプリント)がnとして増加するため2なので、nを2倍にすると、メモリ使用量が4倍になります。 exact関係からの逸脱(asymptoticとは対照的に)は、さまざまな要因(例:malloc / std::allocatorvectorで使用されるブロックサイズ倍増方法)

2)メモリが不足し始めているため、Linuxはpageを開始しています。使用する --pages-as-heap=yestotal(アクティブ+ページング)メモリ使用量を表示する場合。 (ソース: http://valgrind.org/docs/manual/ms-manual.html

32
meowgoesthedog