Soru C ++ bellek miktarını anlamak tahsis ediyor


Ben c ++ 'da yığın üzerinde tahsis edilen bellek miktarını daha iyi anlamaya çalışıyorum. Temel olarak bir dizi 2B vektörü doldurmaktan başka hiçbir şey yapmayan küçük bir test programı yazdım. Bunu bir linux 64bit VM'de çalıştırıyorum ve belleği izlemek için valgrind'in masif aracını kullanıyorum.

Bu testi çalıştırdığım ortam: Win10'da VirtualBox'ta çalışan Linux VM. VM yapılandırması: Temel bellek: 5248MB, 4CPU, kapak 100%, disk tipi VDI (dinamik olarak yerleştirilmiş depolama alanı).

c ++ bellek profili test programı:

/**
 * 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);
        }
    }
}

Bellek profillerini farklı değerlerde ayıklamak için aşağıdaki betik betiğini çalıştırıyorum. n (test.o yukarıdaki programdır, g ++ -std = c ++ 11 ile derlenmiş, g ++ sürüm 5.3.0'dır)

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

Bu bana aşağıdaki sonuçları veriyor:

|--------------------------------|
| 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                |
|--------------------------------|

Her matris n ^ 2 boyutunda olacak, toplam 8 matrisim var, dolayısıyla bir bellek kullanımının olması bekleniyordu. f(n) = 8 * n^2.

Soru 1 N = 250'den n = 8000'e kadar niçin bellek kullanımı n * = 2'de 4 ile çarpılıyor?

N = 16000'den n = 32000'e kadar çok tuhaf bir şey oluyor çünkü valgind aslında bir bellek azalmasını rapor ediyor.

soru 2 N = 16000 ve n = 32000 arasında neler olup bittiğinde, yığın belleğin daha az olması, teoride daha fazla verinin ayrılması nasıl mümkün olabilir?

N = 16000 ve n = 32000 için massif-visualizer çıktısının altına bakınız.

n=16000 n=32000


21
2017-08-21 13:54


Menşei


Ya sahibim sonuçlarınızı çizdi - YSC
Bu soruyu testinizin üzerinde çalıştığınız derleyici ve platformla etiketlemenizi tavsiye ederim. Bellek tahsisinin iç mekanizmaları sizin uygulamanız tarafından belirlenir, c ++ konuyla ilgili birkaç itirazda bulunur. - François Andrieux
Benim anlayışım nasıl vector hafızasını yönetir derleyiciye bir ölçüde bağlıdır. Tipik olarak bellek yönetimi vector hafıza tahsisi için bir miktar ön tahsisatın yanı sıra bir dereceye kadar tahmin içerir. şuna baksana frogatto.com/2009/11/17/how-cs-vector-works-the-gritty-details - Richard Chambers
Push_back'in kullanılması, aradığınız istatistikleri gerçekten değiştirir, bunun nedeni çoğunlukla alanın bittiği zaman dizilerin kopyalanması için sürekli olarak yer ayırması gerektiğidir. Gerçek tahsiyi ölçmenin daha iyi bir yolunun .resize veya .reserve kullanmak olacağını düşünmekteydim. Bu şekilde kopyanın masraflarını ödemediniz. Lütfen anlayışımı ezmek için çekinmeyin ... - Marco
Test profili oluşturma programı push_back'in istatistikleri gerçek profil programına yansıtılmasından daha iyi bir şekilde değiştirmesi durumunda push_back'i kullanan gerçek bir programı yansıtır. Bu, gerçek programımda belleğin nasıl ayrıldığını anlamak için iyi bir şeydir. Ama evet, gerçek bir dünya programında .reserve veya .resize kullanabiliyorsanız, muhtemelen yeniden tahsisat yükünü azaltmak için bunu yapmalısınız, bu konuyla ilgili ayrıntılı bir makalenin burada bulunabileceğine inanıyorum: frogatto.com/2009/11/17/how-cs-vector-works-the-gritty-details - Gio


Cevaplar:


1) Matris vektörlerinizin boyutları (ve dolayısıyla bellek ayak izi) n olarak büyüdüğünden2Böylece n iki katına çıkarılırsa bellek kullanımı dört katına çıkar. Herhangi bir sapma kesin ilişki (aksine asimptotik) farklı faktörlerden dolayı (örneğin, malloc / std::allocatortarafından kullanılan blok boyutu ikiye katlama yöntemi vector)

2) Bellek tükenmeye başlıyorsunuz, bu yüzden Linux başlıyor sayfa bazı; kullanım --pages-as-heap=yes eğer görmek istersen Genel Toplam (etkin + paged) bellek kullanımı. (Kaynak: http://valgrind.org/docs/manual/ms-manual.html)


29
2017-08-21 14:08



Testimi n = 16000'de --pages-as-heap = evet seçeneğiyle çalıştırmayı denedim, maalesef bu durum valginin çökmesine neden oluyor. Çok sayıda "iş parçacığı # 1'de taşma payı taşması, 0x4a2c000'e yetişemez" hatalarıyla başlar, böylece sonuçta bellek yetersiz kalır ve çöker. - Gio
@Gio belki bu gönderi sana yardım edeceğim - meowgoesthedog
Ayrıca en son Valgrind, 3.13, bellek sınırlamalarını biraz rahatlattı. itibaren valgrind.org/docs/manual/dist.news.html: The amount of memory that Valgrind can use has been increased from 64GB to 128GB. In particular this means your application can allocate up to about 60GB when running on Memcheck. - Paul Floyd