Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Różna ilość pamięci alokowanej dla tablicy zmiennych a wskaźników na zmienne

Ostatnio zmodyfikowano 2019-09-29 17:38
Autor Wiadomość
dlakin95
Temat założony przez niniejszego użytkownika
Różna ilość pamięci alokowanej dla tablicy zmiennych a wskaźników na zmienne
» 2019-09-28 20:45:56
Cześć.

Mam pytanie dotyczące ilości pamięci RAM alokowanej dla tablic dynamicznych.

Przeprowadziłem testy tego, ile pamięci RAM zaalokuje dla procesu system operacyjny dla następujących przypadków:

Dla jasności zabrany przez proces ram sprawdzałem Task Menagerem systemu Windows.

I:
C/C++
uint * arr = new uint[ 10 000 000 ]; // Dla tego przypadku pamiec nie zmienila sie, bo nie inicjalizowalem zmiennych
Pamięć: ~13MB

II:
C/C++
uint * arr = new uint[ 10 000 000 ];
for( uint i = 0; i < 10 000 000; i++ ) // Dla tego przypadku inicjalizowalem tablice
    ( *( arr + i ) ) = i;

Pamięć: ~51MB. Można się było spodziewać, ponieważ 10 000 000 * 4B + 4B(na wskaźnik na stosie) = ~40MB. I to jest jasne

ALE:
III:
C/C++
uint ** arr = new uint *[ 10 000 000 ];
for( uint i = 0; i < 10 000 000; i++ ) // Przypadek tablicy wskaźników (Incicjalizacja nie ma znaczenia, zabiera tyle samo pamieci)
    ( *( arr + i ) ) = new uint( i );

Pamięć: ~205MB. Stosując się do sposobu obliczeń drugiego przypadku otrzymujemy: 10 000 000 * 4B(rozmiar wskaźników, już na stercie) + 10 000 000 * 4B(Pamięć dla zmiennych alokowanych dla każdego wskaźnika) + 4B(wskaźnik na stosie na tablice) = ~80MB

To jest właśnie moje pytanie do was:

Jak i dlaczego obliczyć rozmiar alokowanej dynamicznie tablicy wskaźników?
P-175252
pekfos
» 2019-09-28 22:12:25
Jak i dlaczego obliczyć rozmiar alokowanej dynamicznie tablicy wskaźników?
Tak samo, jak dla intów. Skok w zużyciu pamięci bierze się z tych małych alokacji. Jeśli zaalokujesz dynamicznie 1 bajt, to nie ma takiej możliwości, że zużyjesz tylko 1 bajt pamięci. Ile dokładnie, to zależy od implementacji. Może być nawet kilkadziesiąt bajtów. Co może być w tych dodatkowych bajtach każdej alokacji, możesz przeczytać tu http://www.nobugs.org​/developer/win32​/debug_crt_heap.html.
P-175254
dlakin95
Temat założony przez niniejszego użytkownika
» 2019-09-28 23:29:17
W takim razie jak się można zabezpieczyć przed przekroczeniem sterty?
P-175256
DejaVu
» 2019-09-29 12:15:40
Możesz skompilować aplikację jako 64 bitową i wówczas będziesz miał dostępnego tyle ramu ile system da radę przydzielić. Jednak... warto przemyśleć czy poprawnie dane organizujesz w pamięci, jeżeli Twoja aplikacja zużywa więcej jak 1GB RAM i nie robi nic specjalnego.
P-175257
dlakin95
Temat założony przez niniejszego użytkownika
» 2019-09-29 13:28:24
W sumie aplikacja nad którą pracuję może zużywać bardzo duże ilości pamięci, dlatego chciałbym się zabezpieczyć przed przekroczeniem sterty. Albo dowiedzieć się jak lepiej organizować pamięć.

Aplikacja odczyta z bazy danych obiekty, następnie je "skompiluje, a program będzie reagował na zdarzenia i realizował funkcje już skompilowanych obiektów.
P-175258
DejaVu
» 2019-09-29 13:37:32
Alokacja jednego większego bloku pamięci jest zawsze bardziej efektywna niż alokacja n-małych elementów. Także, jeżeli chcesz mieć maksymalnie optymalną organizację danych w pamięci, to musisz dążyć do posiadania relatywnie dużych bloków danych. Przykład: alokuj std::vector<Element> i zadbaj o to, aby Element nie posiadał żadnych dynamicznie alokowanych danych (czyli np. nie posiadał również std::string). Jeżeli masz zmienną długość tekstu, to std::string-a nie unikniesz. Jeżeli wiesz, że tekst nie będzie dłuższy niż np. 10 znaków, to bardziej opłaca się zdefiniować tablicę 10-znakową, niż pozwalać na to, aby std::string wykonał dynamiczną alokację dla tekstu o długości np. jednego znaku, bo i tak dynamiczna alokacja jednego znaku zje więcej pamięci, niż ta stała 10-znakowa tablica.

/edit:
Jeżeli natomiast wiesz, że teksty mogą być długie (100 znaków i więcej), to poprawnym politycznie będzie użycie takiego std::string (innymi słowy: tu optymalizacja nie wniesie praktycznie żadnej korzyści).
P-175259
dlakin95
Temat założony przez niniejszego użytkownika
» 2019-09-29 13:54:27
Dzięki za odpowiedź. Właśnie też na to wpadłem i zrobiłem szybki test.

Na wstępie powiem, że tak kodu w aplikacjach nie piszę tylko na potrzeby testów.

C/C++
QTimer::singleShot( 5000,[]()->void {
    uint ** i = new uint *[ 1000000 ];
    for( uint j = 0; j < 1000000; j++ ) {
        ( *( i + j ) ) = new uint[ 10 ];
        for( uint ii = 0; ii < 10; ii++ )
            ( *( *( i + j ) + ii ) ) = ii;
       
    }
} );
EDIT. A pamięć: 60 MB !!! I z tego materiału, co podrzuciliście wynika, że nie opłaca się alokować pojedynczych wskaźników, bo każdej alokowanej pamięci dochodzi 40 Bajtów na kontrole błędów sterty itd.

Zaalokowałem tablice o rozmiarze 1 000 000 (czyli mniejszą o 10x), a każdy wskaźnik jest tablicą dynamiczną o rozmiarze 10.

Jednak chyba jedynym nie fajnym problemem jest tutaj to, że momencie alokowania obiektów klasy np. 60-bajtowych. Tworzę obiekty, które muszą określić jako niezainicjalizowane.
Ewentualnie dobrze kontrolować rozmiar tablicy.
P-175260
pekfos
» 2019-09-29 14:48:15
Na wstępie powiem, że tak kodu w aplikacjach nie piszę tylko na potrzeby testów.
Dobrze wiedzieć że na produkcji nie wylewasz pamięci do Wisły i nie używasz tych dziwnych zapisów typu ( *( i + j ) ), zamiast takiego na przykład i[j].

I z tego materiału, co podrzuciliście wynika, że nie opłaca się alokować pojedynczych wskaźników, bo każdej alokowanej pamięci dochodzi 40 Bajtów na kontrole błędów sterty itd.
Jedyny przypadek, w którym jest sens alokować jeden element dynamicznie to tworzenie obiektów klas z metodami wirtualnymi. No i może jeszcze bym dał wyjątek od tej reguły dla typów których nie można domyślnie skonstruować, albo gdy trzeba ponownie tworzyć obiekty. Ale generalnie nie ma sensu. Nie tylko przez narzuty pamięciowe, ale też przez wydajność. Jest coś takiego jak spatial cache locality. Dostęp do pamięci RAM jest bardzo wolny, a procesory potrafią optymalizować przewidywalne schematy dostępów do pamięci - takie jak: odczyt kolejnych komórek w tablicy. Ale jeśli zamiast intów będziesz w tablicy przechowywać wskaźniki na dynamicznie zaalokowane inty, to dostęp do ich wartości nie jest przewidywalny i może być wolniejszy o całe rzędy wielkości.
P-175261
« 1 » 2
  Strona 1 z 2 Następna strona