Jak duży może być malloc w C?

Mam malloc w C czyli 26901^2*sizeof (double)

To dało mi do myślenia, jaka może być tu największa wartość?

Również, czy miałbym jakieś problemy z zdefiniowaniem makra, aby uzyskać dostęp do tej tablicy 2D?

 #define DN(i,j) ((int)i * ny + (int)j)

Ponieważ wydaje się, że to nie działa dla mnie-lub przynajmniej nie jestem pewien, że jest. Nie mogę wymyślić,jak zrobić totalview dive na makrze, aby powiedzieć mi, na co faktycznie patrzy[DN(indx, jndx)].

Author: Derek, 2010-08-12

6 answers

Obserwacje

Zakładając typowy alokator, taki jak ten, którego używa glibc, istnieją pewne obserwacje:

  1. niezależnie od tego, czy pamięć jest rzeczywiście używana, region musi być bezpośrednio zarezerwowany w pamięci wirtualnej.
  2. największe wolne sąsiadujące ze sobą regiony zależą od wykorzystania pamięci istniejących regionów pamięci i dostępności tych regionów do malloc.
  3. praktyki mapowania zależą od architektury i systemu operacyjnego. Ponadto te praktyki wpływają na podstawowe wywołania systemowe w celu uzyskania regionów pamięci (np. mmap pozyskiwanie stron).

Eksperyment

Oto prosty program do przydzielania największego możliwego bloku (kompilacji z gcc largest_malloc_size.c -Wall -O2:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void *malloc_wrap(size_t size)
{
    void *p = malloc(size);
    if (p) {
        printf("Allocated %zu bytes from %p to %p\n", size, p, p + size);
    }
    else {
        printf("Failed to allocated %zu bytes\n", size);
    }
    return p;
}

int main()
{
    size_t step = 0x1000000;
    size_t size = step;
    size_t best = 0;
    while (step > 0)
    {
        void *p = malloc_wrap(size);
        if (p) {
            free(p);
            best = size;
        }
        else {
            step /= 0x10;
        }
        size += step;
    }
    void *p = malloc_wrap(best);
    if (p) {
        pause();
        return 0;
    }
    else {
        return 1;
    }
}

Uruchomienie powyższego programu (./a.out) na mojej Maszynie Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux uzyskuje taki wynik:

<snip>
Allocated 2919235584 bytes from 0x9763008 to 0xb7763008
Allocated 2936012800 bytes from 0x8763008 to 0xb7763008
Failed to allocated 2952790016 bytes
Failed to allocated 2953838592 bytes
Failed to allocated 2953904128 bytes
Failed to allocated 2953908224 bytes
Allocated 2936012800 bytes from 0x85ff008 to 0xb75ff008

To jest przydział dokładnie 2800MiB. Obserwując odpowiednie odwzorowanie z /proc/[number]/maps:

<snip>
0804a000-0804b000 rw-p 00001000 08:07 3413394    /home/matt/anacrolix/public/stackoverflow/a.out
085ff000-b7600000 rw-p 00000000 00:00 0          [heap]
b7600000-b7621000 rw-p 00000000 00:00 0 
b7621000-b7700000 ---p 00000000 00:00 0 
b7764000-b7765000 rw-p 00000000 00:00 0 
b7765000-b78b8000 r-xp 00000000 08:08 916041     /lib/tls/i686/cmov/libc-2.11.1.so
<snip>
bfc07000-bfc1c000 rw-p 00000000 00:00 0          [stack]

Podsumowanie

Wygląda na to, że sterta została rozszerzona w obszarze pomiędzy danymi programu i kodem, a mapowaniami bibliotek współdzielonych, które są dopasowane do granicy przestrzeni pamięci użytkownika / jądra (oczywiście 3G/1g w tym systemie).

Ten wynik sugeruje, że maksymalna przestrzeń alokowana za pomocą malloca jest w przybliżeniu równa:

    W tym przypadku nie jest to możliwe.]}
  1. minus przesunięcie do początku the heap (program code and data)
  2. mniej miejsca zarezerwowanego dla głównego stosu wątków
  3. mniej miejsca zajmowanego przez wszystkie zmapowane biblioteki współdzielone
  4. W zależności od tego, która z tych wartości jest większa, można ją znaleźć w obszarze dostępnym dla sterty (który może być fragmentowany przez inne mapowania).]}

Uwagi

W odniesieniu do implementacji glibc i Linuksa, następujące fragmenty podręcznika są świetne zainteresowanie:

malloc

   Normally, malloc() allocates memory from the heap, and adjusts the size
   of the heap as required, using sbrk(2).  When allocating blocks of mem‐
   ory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation
   allocates the memory as a  private  anonymous  mapping  using  mmap(2).
   MMAP_THRESHOLD  is  128  kB  by  default,  but is adjustable using mal‐
   lopt(3).

mmap

   MAP_ANONYMOUS
          The mapping is not backed by any file; its contents are initial‐
          ized to zero.

Afterword

Ten test został wykonany na jądrze x86. Spodziewałbym się podobnych rezultatów po jądrze x86_64, aczkolwiek z zwróconymi znacznie większymi regionami pamięci. Inne systemy operacyjne mogą różnić się rozmieszczeniem mapowań i obsługą dużych malloc s, Więc wyniki mogą być znacznie różne.

 36
Author: Matt Joiner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-15 08:17:24

To zależy od twojej implementacji malloc!

Według Wikipedii, " od wydania v2.3 biblioteka GNU C (glibc) używa zmodyfikowanego ptmalloc2, który sam jest oparty na dlmalloc v2.7.0."dlmalloc odnosi się do implementacji malloc Douga Lea. Ważną rzeczą do odnotowania w tej implementacji jest to, że duże mallocs są realizowane poprzez funkcjonalność plików mapowanych w pamięci systemu operacyjnego, więc bloki te mogą być dość duże rzeczywiście bez wielu problemów ze znalezieniem sąsiadujący blok.

 9
Author: Hut8,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 22:08:49

Odpowiedź na pytanie malloc (zależy od systemu operacyjnego, którego nie podajesz), więc o tym Definiuj:

#define DN(i,j) ((int)i * ny + (int)j)

Nie jest do końca bezpieczne, bo ktoś może zrobić DN(a+b,c) co rozszerza się do

((int)a+b * ny + (int)c)
Prawdopodobnie nie tego chciałeś. Więc Wstaw tam dużo nawiasów:
#define DN(i,j) ((int)(i) * ny + (int)(j))

Aby zobaczyć na co DN(indx,jndx) wskazuje, wystarczy printf("%d\n",DN(indx,jndx));

 7
Author: mvds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 22:14:55

Parametr size w wywołaniu malloc jest typu size_t, który różni się w zależności od implementacji. Zobacz to pytanie Po Więcej.

 1
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:21
To dało mi do myślenia, jaka może być tu największa wartość?

26'901^2 = 723'663'801. Jeśli twój podwójny ma 8 bajtów, to jest mniejszy niż 8GB. Nie widzę żadnego problemu z przydzieleniem tyle pamięci, a moje aplikacje rutynowo przydzielają (na systemach 64 bitowych) znacznie więcej. (Największe zużycie pamięci jakie kiedykolwiek widziałem to 420gb (w systemie Solaris 10 numa z 640GB RAM) z największym ciągłym blokiem ~24GB.)

Największa wartość jest trudna do zidentyfikowania, ponieważ jest platformą dependent: podobnie jak w systemach 32bit, zależy od podziału przestrzeni użytkownika / jądra. W obecnym stanie rzeczy, myślę, że najpierw dojdzie się do limitu rzeczywistej fizycznej pamięci RAM-zanim osiągnie się limit tego, co libc może przydzielić. (A kernel ma to gdzieś, po prostu rozszerza pamięć wirtualną często, nawet nie zastanawiając się, czy jest wystarczająca ilość pamięci RAM, aby ją przypiąć.)

 1
Author: Dummy00001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 22:21:54

Największy blok pamięci, jaki możesz zapytać malloc() for jest największą wartością {[2] } - jest to SIZE_MAX z <limits.h>. Największa ilość, jaką można pomyślnie żądanie jest oczywiście zależna od systemu operacyjnego i konfiguracji poszczególnych maszyn.

Twoje makro nie jest bezpieczne. Wykonuje obliczenia indeksu za pomocą zmiennej int, która jest wymagana tylko do zakresu do 32767. Każda wartość wyższa od tej może spowodować przepełnienie podpisu, co skutkuje nieokreślone zachowanie. Prawdopodobnie najlepiej będzie wykonać obliczenia jako size_t, ponieważ ten typ musi posiadać dowolny poprawny indeks tablicy:

#define DN(i, j) ((size_t)(i) * ny + (size_t)(j))

(chociaż zauważ, że jeśli podasz wartości ujemne dla i lub j, otrzymasz indeks daleko poza granicami).

 1
Author: caf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 23:52:53